﻿using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Globalization; // DateTime.TryParseExactに使用
using System.IO;
using System.Linq;
using System.Management; //VSで参照追加必要
using System.Net.NetworkInformation;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.ServiceProcess;
using System.Xml;
using System.Xml.Linq;
//using System.DirectoryServices;
using static System.Net.Mime.MediaTypeNames;




/*
ObservePC バージョン履歴

2025.10.16 Ver1.0
- Windows 10/11 + .NET Framework4.7.2 対応のコンソールツール
- WMIを用いてPC環境情報を取得
- AIとの対話時に前提整形として活用
- GUI不要、コピーペースト最適化
- 改変・配布自由（不正改変は禁止）

2025.10.17 Ver1.1
- user_note.txt の連結機能を追加
- 起動オプションによる属人属性の明示（-L, -H, -M）
- モニター情報取得機能を追加

2025.10.21 Ver1.2
- 以下の機能を追加：
  ・スタートアップ一覧
  ・Windows Update履歴
  ・NIC構成
  ・常駐プロセス一覧

2025.10.22 Ver1.3
- 以下の修正
常駐プロセス一覧がアルファベット順に出てるだけ。これでは意味をなさない
メソッドを書き換えて、サードパーティ製プロセス一覧（常駐候補）へ切り替え

2025.10.24 Ver1.4
- 以下の修正
ターゲットフレームワーク4.7.2から4.8へ変更
取得情報の強化。BIOS情報、NIC情報拡張、OSインストール日、セキュリティ状況(セキュリティソフトの推測)、タスクスケジューラ登録
管理・教材用途を考慮してLAN出力（SMB/HTTP/Syslog/SMTP）のコメントアウト＋テンプレート追加

2025.11.7 Ver1.6
Ver1.5での失敗を踏まえて、互換性の確保に注力 PowerShell依存を完全撤廃
ターゲットフレームワーク4.7.2へ再び変更。Server2019で動作確認
サービスの出力を追加。JSONはLAN出力同様にコメントアウト
サービス一覧（異常・停止・無効)を追加

2025.11.7 Ver1.65
ファイアウォール通過許可ルール（残骸検出）をユーザー/アプリが設定したファイアウォールルールへ変更

2025.11.12 Ver1.66
タスクスケジューラーとサービス情報を改修

 2025.11.7 Ver1.67
 情報取得を並列実行へ変更 高速モード-F新設

 2025.11.16 Ver1.68
 並列処理の補完として逐次実行を追加 安全モード-S新設
 モード指定無し・-Eモード時に取得確認と補完追加

 2025.11.17 Ver1.69
 -Sモードで逐次取得を2回るように改良

2026.1.18 Ver1.75
マスターテーブル構造へ変更とメソッドの引数無しで統一
イベントログの出力件数を増加
2026.1.22 Ver1.75
NTP,電源プランの表示を追加

2026.1.25 Ver1.8
リリースバージョン。
マスタテーブルから抜けていたのを発見。修正
ウォームアップ処理の追加(コメントアウト)
Windows Update履歴の出力をインストール日でソート

2026.1.31 Ver1.81
バグフィクス。実行速度の最適化

2026.2.3 Ver1.9
異常ドライバー検出→ドライバー一覧へ変更
外部マスターテーブル(ObservePC.cfg)と最大出力件数・モード強制(ObservePC.out)の試験実装

2026.2.9 Ver2.0
外部設定ファイルの仕様確定。実装
取得機能追加、バグフィクス

2026.2.19 Ver2.1
外部ファイルでExpertMode = trueの場合に外部ファイルで指定した出力上限が反映せず30になってしまうバグを修正
マスターテーブルの初期値の見直し

 */




// Win32 API 関数と構造体の定義
class Program
{
    //グローバル(クラス内)変数の宣言
    static ConcurrentDictionary<int, string> sb_data = new ConcurrentDictionary<int, string>(); //メソッドからの値格納用
    static Dictionary<int, string> sb_Comment = new Dictionary<int, string>(); //sbのインデックス
    static Dictionary<int, string> sb_Method_Name = new Dictionary<int, string>(); //sbの取得メソッド
    static Dictionary<int, byte> WmiLevel = new Dictionary<int, byte>(); //WMIの使用量（0-3）
    static Dictionary<int, byte> LoadLevel = new Dictionary<int, byte>(); //メソッドの処理負荷（1-4）
    static Dictionary<int, byte> ConflictGroup = new Dictionary<int, byte>(); //競合グループ（0-8）

    static byte MaxMethod = 0;   //取得メソッド(セクション)数
    static byte MaxDataOutput = 10; // 通常モード出力上限は10件
    const byte MaxDataOutput_E = 30;    //-Eモード出力上限は30件
    static bool expertMode; //-E,-F,-Sモード(拡張表示) フラグ
    static bool IsExtended;    //-E,-F,-Sモードで呼び出されるメソッドのフラグ
    static bool Ran_Sw;         //実際に取得のON/OFF
    static bool SafetyMode;
    static bool FastMode;
    const String Ver = "ObservePC 2.1";      //プログラム名バージョン表記



    // ディスプレイ情報を取得するためのWin32 API構造体（DISPLAY_DEVICE）
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct DISPLAY_DEVICE
    {
        public int cb; // 構造体のサイズ (Marshal.SizeOf(DISPLAY_DEVICE)で初期化)
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DeviceName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceString; // デバイスのユーザーフレンドリーな名前
        public int StateFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceKey;
    }

    // user32.dllからEnumDisplayDevices関数をインポート
    // これにより、Win32 APIのネイティブな機能（GPU/モニター情報取得）をC#から呼び出せる
    [DllImport("user32.dll", CharSet = CharSet.Ansi)]
    public static extern bool EnumDisplayDevices(
        string lpDevice,
        uint iDevNum,
        ref DISPLAY_DEVICE lpDisplayDevice,
        uint dwFlags
    );

    // インストール済みアプリ情報を格納するシンプルなクラス
    class InstalledApp
    {
        public string Name { get; set; }
        public DateTime? InstallDate { get; set; } // インストール日 (取得できない場合もあるためNullable)
    }


    // プログラムのエントリポイント（メイン処理）
    // 通常Mainは改変する必要は無い
    static async Task Main(string[] args)
    {
        DateTime dt1 = DateTime.Now;    //時刻取得
        Console.WriteLine(dt1);
        //軽い処理を実行してキャッシュしながら外部設定ファイルを読み込む
        var warmTask = Task.Run(() => WarmUp());
        var cfgTask = Task.Run(() => LoadNewCfg());
        await Task.WhenAll(warmTask, cfgTask);

        //-Eモード検出
        expertMode = args.Contains("-E", StringComparer.OrdinalIgnoreCase); 
        foreach (string arg in args)
        {
            if (arg.Equals("-E", StringComparison.OrdinalIgnoreCase))
            {
                MaxDataOutput = MaxDataOutput_E;         //-Eでの表示件数を規定。
            }
        }

        //-Fモード検出
       FastMode = args.Contains("-F", StringComparer.OrdinalIgnoreCase);
        foreach (string arg in args)
        {
            if (arg.Equals("-F", StringComparison.OrdinalIgnoreCase))
            {
                //WarmUp();   //軽い処理を実行してキャッシュ
                expertMode = true;
                MaxDataOutput = MaxDataOutput_E;
            }
        }

        //-Sモード検出
        SafetyMode = args.Contains("-S", StringComparer.OrdinalIgnoreCase);
        foreach (string arg in args)
        {
            if (arg.Equals("-S", StringComparison.OrdinalIgnoreCase))
            {
                expertMode = true;
                MaxDataOutput = MaxDataOutput_E;
            }
        }
        //Out_Reading();  //外部ファイルの指示読み込み反映
        ApplyCfg();      //新外部ファイル指示を反映
        // StringBuilderは、文字列を効率よく結合するためのクラス
        var sb = new StringBuilder(8192);
        string hed = Ver;
        hed = hed + " :" + dt1 + " :" + TimeZoneInfo.Local;
        sb.Append (hed + " :");
        sb.AppendLine();
        TimeZoneInfo tz = TimeZoneInfo.Local;
        InitializeMethodMaps();

        //出力見出しをマスターテーブル(sb_Comment)から整形
        string[] sectionHeaders = sb_Comment
            .OrderBy(kv => kv.Key)
            .Select(kv => "◆ " + kv.Value + " ◆")
            .ToArray();

        /*
            string[] sectionHeaders = new[]
            {
        "?? OS情報：",
        "?? マザーボード情報：",
        "?? CPUとメモリー情報：",
        "?? ストレージ情報 (容量と空き容量)：",
        "?? 接続されているデバイス一覧（分類・最大10件）：",
        "?? GPU情報：",
        "?? ディスプレイ情報（EnumDisplayDevicesより）：",
        "?? ネットワーク構成（System.Net + WMIより）：",
        "?? 環境変数 (Path, Tempなど)：",
        "?? Windows Update履歴（KB番号とインストール日）：",
        "?? 最近インストールされたソフトウェア（最大10件）：",
        "?? スタートアップアプリ一覧：",
        "?? サードパーティ製プロセス一覧（常駐候補）：",
        "?? セキュリティソフトの常駐状況（推定）：",
        "?? ファイアウォール概要",
        "?? 異常ドライバー検出:",
        "?? 最近のシステムイベントログ（Error/Warningのみ、10016除外 最大10件）：",
        "?? ユーザーアカウント一覧：",
        "?? タスクスケジューラ登録一覧（整形済・schtasks.exeより）：",
        "?? 削除ソフト痕跡（レジストリより推定）：",
        "?? 削除ソフト痕跡（イベントログより確定）：",
        "?? サービス一覧（異常・停止・無効)：",
        "?? ユーザー/アプリが設定したファイアウォールルール:"
    };
         */

        //並列取得
        if (!SafetyMode)
        {

            if (FastMode)   //-F無制限並列取得へ
            {
                var result = await GetAllSystemDataFastAsync(expertMode);
                sb.Append(result);
            }
            else　　//オプション無し-Eモード多段限定並列取得へ
            {
                var result = await GetAllSystemDataAsync_E(expertMode);
                sb.Append(result);
            }          
        }

        //逐次取得、FastMode除く
        if (!FastMode)  //オプション無し-Eモードの2回目と-Sモードの取得
        {
            byte i = 0;
            do
            {
                GetSequential();
                i++;
                //Console.WriteLine(i);
            } while ((i < 2)&&(SafetyMode));    //SafetyModeだけ2回取得

        }

        //連結
        // すべてのsb_dataを連結（空欄はスキップ）
        //sb.AppendLine("==== 統合出力 ====");
        //int MaxIndex = 30;
        for (int i = 0; i < MaxMethod; i++) // メソッド数までループ
        {
            if (sb_data.TryGetValue(i, out string value) && !string.IsNullOrWhiteSpace(value))
            {
                string trimmed = value.TrimEnd(); // ← 末尾の改行を除去
                sb.AppendLine(trimmed);
                sb.AppendLine(); // ← 1行だけ追加
            }

        }

        //ユーザー補足情報ファイル (user_note.txt) の読み込み
        string noteFile = "user_note.txt";
        if (System.IO.File.Exists(noteFile))
        {
            sb.AppendLine("== 使用者補足情報（user_note.txt より） ==");
            // UTF8で読み込み (日本語文字化け対策)
            string[] notes = System.IO.File.ReadAllLines(noteFile, Encoding.UTF8);
            foreach (string line in notes)
            {
                sb.AppendLine(line);
            }
        }

        //起動オプションによる属人属性（AI応答プロトコルヘッダー）の追加
        List<string> userNotes = new List<string>();

        foreach (string arg in args)
        {
            // -L: Learner (初心者)
            if (arg.Equals("-L", StringComparison.OrdinalIgnoreCase))
            {
                userNotes.Add("--- [AI応答プロトコルヘッダー] ---");
                userNotes.Add("説明要求レベル: 初心者 (-L)");
                userNotes.Add("AIへの説明要求: 初心者です。専門用語を避け、平易に説明してください。");
            }
            // -H: Highly experienced (経験者)
            else if (arg.Equals("-H", StringComparison.OrdinalIgnoreCase))
            {
                userNotes.Add("--- [AI応答プロトコルヘッダー] ---");
                userNotes.Add("説明要求レベル: 経験者 (-H)");
                userNotes.Add("AIへの説明要求: 経験者です。簡潔に、要点だけを伝えてください。");
            }
            // -M=message: カスタムメッセージ
            else if (arg.StartsWith("-M=", StringComparison.OrdinalIgnoreCase) && arg.Length > 3)
            {
                string rawMessage = arg.Substring(3);
                // 引用符(' " ')やスペースをトリムしてメッセージを整形
                string message = rawMessage.Trim('"', '\'', ' ');

                if (!string.IsNullOrEmpty(message))
                {
                    userNotes.Add("--- [AI応答プロトコルヘッダー] ---");
                    userNotes.Add("説明要求レベル: 標準 (-M) + カスタム");
                    userNotes.Add($"AIへの説明要求: {message}");
                }
            }
        }

        if (userNotes.Count > 0)
        {
            sb.AppendLine();
            sb.AppendLine("== 起動オプションによる使用者属性 ==");
            foreach (string line in userNotes)
            {
                sb.AppendLine(line);
            }
        }


        // == 相談開始宣言 ==
        sb.AppendLine("以上の環境で相談があります");

        string machine = Environment.MachineName;
        string date = DateTime.Now.ToString("yyyyMMdd");

        string finalOutput = Regex.Replace(sb.ToString(), @"\x00|\x01|\x02|\x03|\x04|\x05|\x06|\x07|\x08|\x0B|\x0C|\x0E-\x1F", "");

        string txtFile = $"PC_Observation_{machine}_{date}.txt";
        string jsonFile = $"PC_Observation_{machine}_{date}.json";

        var snapshot = new Dictionary<string, string>();
        string sbText = finalOutput; // sb.ToString() 後の整形済みテキスト

        foreach (string header in sectionHeaders)
        {
            string key = header.Trim('?', '：').Trim();
            string sectionText = ExtractSection(sbText, header);
            snapshot[key] = sectionText;
        }



        // 並列出力処理（Task.Runベース）
        PerformOutput(sbText, snapshot, txtFile, jsonFile);
        /*
                var outputTasks = new List<Task>
        {
            Task.Run(() => Console.Write(sbText)),
            Task.Run(() => File.WriteAllText(txtFile, sbText,new UTF8Encoding(false))),
            Task.Run(() => {
            try
            {
                var json = Newtonsoft.Json.JsonConvert.SerializeObject(snapshot, Newtonsoft.Json.Formatting.Indented);
                File.WriteAllText(jsonFile, json, Encoding.UTF8);
                Console.WriteLine($"JSON出力完了: {jsonFile}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"JSON出力失敗: {ex.Message}");
            }
        })


        };

                try
                {
                    Task.WaitAll(outputTasks.ToArray());
                }
                catch (Exception ex)
                {
                    Console.WriteLine("出力処理中にエラーが発生しました: " + ex.Message);
                }
        */

        // == 転送テンプレート（必要に応じて有効化） ==
        // 必要に応じて、以下の関数を有効化・改変してください

        // SMB共有フォルダへコピー
        // OutputSender.SendToSharedFolder(fileName);

        // HTTP POST送信
        // OutputSender.SendToHttpServer(fileName);

        // FTPアップロード
        // OutputSender.SendToFtpServer(fileName);

        // Syslog送信（UDP 514）
        // OutputSender.SendToSyslog(fileName);

        // SMTP送信（LAN内・ポート25・認証なし）
        // OutputSender.SendViaSmtp_LAN(fileName);



        // 終了待機 LAN出力転送するならコメントアウト推奨
        DateTime dt2 = DateTime.Now;    //時刻取得
        Console.WriteLine(dt2);
        TimeSpan timeSpan = dt2 - dt1;
        Console.Write("処理時間 :");
        Console.WriteLine(timeSpan);
        Console.WriteLine("完了しました。何かキーを押すと終了します。");
        Console.ReadKey();
    }



    //出力メソッド
    static void PerformOutput(string sbText, Dictionary<string, string> snapshot, string txtFile, string jsonFile)
    {
        // JSON出力先の判定（FAT32なら一時フォルダへ退避）
        /*
        string jsonFullPath = Path.GetFullPath(jsonFile);
        string jsonTarget = jsonFullPath;

        try
        {
            DriveInfo drive = new DriveInfo(Path.GetPathRoot(jsonFullPath));
            if (drive.DriveFormat.Equals("FAT32", StringComparison.OrdinalIgnoreCase))
            {
                Console.WriteLine("FAT32環境のため、JSON出力先を一時フォルダに変更します。");
                jsonTarget = Path.Combine(Path.GetTempPath(), Path.GetFileName(jsonFile));
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ドライブ判定に失敗しました（相対パスの可能性）: " + ex.Message);
            // jsonTarget はそのまま jsonFullPath を使う
        }
        */
        // 並列出力処理
        var outputTasks = new List<Task>
        {
        Task.Run(() => Console.Write(sbText)),
        Task.Run(() => File.WriteAllText(txtFile, sbText, new UTF8Encoding(false))),
        // JSON出力（Ver1.6ではコメントアウト）

        /*
        Task.Run(() => {
            try
            {
                var cleanSnapshot = snapshot.ToDictionary(
                    kv => kv.Key,
                    kv => Regex.Replace(kv.Value ?? "", @"[\x00-\x1F]", "")
                );

                var json = Newtonsoft.Json.JsonConvert.SerializeObject(cleanSnapshot, Newtonsoft.Json.Formatting.Indented);
                File.WriteAllText(jsonTarget, json, new UTF8Encoding(false));
                Console.WriteLine($"JSON出力完了: {jsonTarget}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"JSON出力失敗: {ex.GetType().Name} - {ex.Message}");
            }
        })
        */
        };

        try
        {
            Task.WaitAll(outputTasks.ToArray());
        }
        catch (AggregateException ae)
        {
            foreach (var ex in ae.InnerExceptions)
            {
                Console.WriteLine("出力タスク例外: " + ex.GetType().Name + " - " + ex.Message);
            }
        }
    }

    //辞書の初期値入力
    //この部分が唯一のマスターテーブル
    //表記順が出力順となる
    // ------------------------------
    // 拡張者向けガイド
    // 1. 新しい情報取得は OutputXXX() を追加
    // 2. 例外はメソッド内で処理すること
    // 3. mappings に1行追加するだけで反映される
    // 4. 書式:見出し,メソッド名,拡張モードフラグIsExtended, WMI負荷(0-3),処理負荷(1-4)
    // 5. WmiLevel / LoadLevel は並列段生成のための目安値
    /*
    WmiLevel の意味
     	0 = WMI 不使用
     	1 = 軽い（1-2回）
     	2 = 中程度（3-5回）
     	3 = 重い（5回以上 or Win32_ 系複数）
    LoadLevel の意味
     	1 = 軽い
     	2 = 普通
     	3 = 重い
     	4 = 最重量
    ConflictGroup の意味
        0 = 軽量処理。競合なし。軽い WMI や .NET API、環境変数など。
        1 = デバイス列挙系。Win32_PnPEntity など、PnP デバイスの取得。
        2 = ディスク列挙系。Win32_LogicalDisk や DiskDrive など I/O 系。
        3 = ネットワーク列挙系。Win32_NetworkAdapter や IP 構成取得。
        4 = サービス列挙系。Win32_Service の大量列挙。
        5 = EventLog 系。System/Application ログの読み込み。
        6 = レジストリ大量読み込み系。Uninstall キー、Run キーなど。
        7 = 外部プロセス起動系。schtasks.exe、netsh.exe など。
        8 = セキュリティ/ドライバ解析系。WMI + フィルタ処理で重いもの。
     Ran_Swの意味
    取得・表示のON/OFF
    */
    // ------------------------------
    // マスターテーブル（唯一の情報源）
    // ConflictGroup を追加した構造
    static (string Comment,
            Func<string> Method,
            bool IsExtended,
            byte WmiLevel,
            byte LoadLevel,
            byte ConflictGroup,
            bool Ran_Sw)[] mappings;

    /*
    cfgファイル対応。コードを書き換えなくても必要な機能だけ動かすカスタマイズ可能
    ObservePC.cfgファイルの書式
    マスターテーブル　("OS情報", OutputOSInfo, false, 1, 2,0)
    cfgファイル　OS情報, OutputOSInfo, false, 1, 2,0

    出力は記入順となります
    ---ObservePC.cfg記入例---
    OS情報, OutputOSInfo, false, 1, 2,0
    ドライバー情報, OutputDriverAnalysis, false, 1, 4,8
    最近のシステムイベントログ, OutputEventLog, false, 0, 4,5
    NTP同期状態, OutputNtpStatus, false, 0, 1,0
    --------------------------
    */
    static void InitializeMethodMaps()
    {
        var defaultMap = new (string, Func<string>, bool, byte, byte, byte, bool)[]
{
    ("PC起動時刻", OutputUptimeInfo, false, 1, 1, 0, true),
    ("コンピュータ名、ドメイン、ユーザー", OutputMachineInfo, false, 0, 1, 0, true),
    ("OS情報", OutputOSInfo, false, 1, 2, 0, true),
    ("システム詳細設定", OutputSystemDetailSettings, false, 0, 1, 0, true),
    ("OneDrive 詳細解析", OutputOneDriveDetail_E, true, 0, 2, 0, true),

    ("マザーボード情報", OutputMotherboardInfo, false, 1, 2, 0, true),
    ("BIOS情報", OutputBIOSInfo, false, 1, 2, 0, true),
    ("CPUとメモリー情報", OutputSystemInfo, false, 1, 3, 0, true),
    ("ストレージ情報", OutputDiskInfo, false, 1, 3, 2, true),
    ("GPU情報", OutputGPUInfo, false, 1, 1, 0, true),
    ("ディスプレイ情報", OutputDisplayInfo, false, 0, 2, 1, true),
    ("ネットワーク構成", OutputNetworkInfo, false, 1, 3, 3, true),
    ("ドメイン / ワークグループ情報", OutputDomainInfo, false, 0, 1, 0, true),

    // --- 情報密度エリア（最適化済み） ---

    ("接続デバイス一覧", OutputDeviceInfo, true, 1, 3, 1, true), // 4→3（高速化）
    ("環境変数", OutputEnvironmentVariables, true, 0, 1, 0, true),
    ("Windows Update履歴", OutputUpdateHistory, false, 1, 3, 0, true),

    ("最近インストールされたソフトウェア", OutputSoftwareInfo, false, 0, 4, 6, true), // 最重量のまま
    ("スタートアップアプリ一覧", OutputStartupApps, false, 1, 4, 6, true), // 最重量のまま

    ("サードパーティ製プロセス一覧", OutputResidentProcesses, true, 0, 2, 0, true), // 3→2（高速化）

    ("セキュリティソフト状況", OutputSecurityStatus, false, 1, 4, 8, true), // 最重量
    ("FW プロファイル状態", GetFirewallProfileStatus, false, 0, 2, 0, true),

    ("ドライバー情報", OutputDriverAnalysis, false, 1, 3, 8, true), // 4→3（高速化）
    ("最近のシステムイベントログ", OutputEventLog, false, 0, 3, 5, true), // 4→3（高速化）

    ("クラッシュ・異常終了サマリ", OutputCrashSummary_E, true, 0, 2, 5, false
),

    ("Edge ブラウザ情報", OutputEdgeInfo, false, 0, 1, 0, true), // 2→1（高速化）
    ("プリンター一覧", OutputPrintersBasic, true, 1, 1, 0, true), // 2→1（高速化）

    // === ExpertMode 専用 ===

    ("ユーザーアカウント一覧", OutputUserAccounts, true, 1, 2, 0, true), // 3→2（高速化）
    ("タスクスケジューラ一覧", GetTaskSchedulerInfo, true, 0, 4, 7, false),
    ("削除ソフト痕跡", OutputUninstallTraces, true, 0, 4, 6, false),

    ("サービス一覧", GetFilteredServices, true, 1, 3, 4, true),
    ("Windows標準以外のサービス一覧", OutputThirdPartyServices, true, 1, 3, 4, false),

    ("FWルール", OutputFirewallRules_E, true, 0, 4, 7, true),

    ("NTP同期状態", OutputNtpStatus, true, 0, 1, 0, true),

    ("Windows Search 状態", OutputWSearchDetail_E, true, 1, 2, 4, false)
};


        /*
        var defaultMap = new (string, Func<string>, bool, byte, byte, byte, bool)[]
        {
    ("PC起動時刻", OutputUptimeInfo, false, 1, 1, 0,true),
    ("コンピュータ名、ドメイン、ユーザー", OutputMachineInfo, false, 0, 1, 0,true),
    ("OS情報", OutputOSInfo, false, 1, 2, 0,true),
    ("システム詳細設定", OutputSystemDetailSettings, false, 0, 1, 0,true), // AI推論に重要なのでfalse維持
    ("OneDrive 詳細解析", OutputOneDriveDetail_E, true, 0, 2, 0,true),

    ("マザーボード情報", OutputMotherboardInfo, false, 1, 2, 0,true),
    ("BIOS情報", OutputBIOSInfo, false, 1, 2, 0,true),
    ("CPUとメモリー情報", OutputSystemInfo, false, 1, 3, 0,true),
    ("ストレージ情報", OutputDiskInfo, false, 1, 3, 2,true),
    ("GPU情報", OutputGPUInfo, false, 1, 1, 0,true),
    ("ディスプレイ情報", OutputDisplayInfo, false, 0, 2, 1,true),
    ("ネットワーク構成", OutputNetworkInfo, false, 1, 3, 3,true),
    ("ドメイン / ワークグループ情報", OutputDomainInfo, false, 0, 1, 0,true),

    // --- ここから「情報密度」の調整エリア ---

    ("接続デバイス一覧", OutputDeviceInfo, true, 1, 4, 1,true),          // 【変更】初心者はUSB機器の羅列を怖がるためtrueへ
    ("環境変数", OutputEnvironmentVariables, true, 0, 1, 0,true),       // 【変更】呪文すぎるので通常時は隠す(true)
    ("Windows Update履歴", OutputUpdateHistory, false, 1, 3, 0,true),
    ("最近インストールされたソフトウェア", OutputSoftwareInfo, false, 0, 4, 6,true),
    ("スタートアップアプリ一覧", OutputStartupApps, false, 1, 3, 6,true),
    ("サードパーティ製プロセス一覧", OutputResidentProcesses, true, 0, 3, 0,true), // 【変更】exeのパス羅列はノイズが多いためtrueへ
    ("セキュリティソフト状況", OutputSecurityStatus, false, 1, 4, 8,true),
    ("FW プロファイル状態", GetFirewallProfileStatus, false, 0, 2, 0,true),
    ("ドライバー情報", OutputDriverAnalysis, false, 1, 4, 8,true),      // Bad判定が出るならfalseでOK
    ("最近のシステムイベントログ", OutputEventLog, false, 0, 4, 5,true),
    ("クラッシュ・異常終了サマリ", OutputCrashSummary_E, true, 0, 2, 5,true),

    ("Edge ブラウザ情報", OutputEdgeInfo, false, 0, 2, 0,true),
    ("プリンター一覧", OutputPrintersBasic, true, 1, 2, 0,true),        // 【変更】プリンターが30台とかある環境だと辛いのでtrueへ

    // === ExpertMode 専用 ===
    ("ユーザーアカウント一覧", OutputUserAccounts, true, 1, 3, 0,true),
    ("タスクスケジューラ一覧", GetTaskSchedulerInfo, true, 0, 4, 7,true),
    ("削除ソフト痕跡", OutputUninstallTraces, true, 0, 4, 6,false),        //マニアックなので標準使用停止
    ("サービス一覧", GetFilteredServices, true, 1, 3, 4,true),
    ("Windows標準以外のサービス一覧", OutputThirdPartyServices, true, 1, 3, 4, true),
    ("FWルール", OutputFirewallRules_E, true, 0, 4, 7,true),
    ("NTP同期状態", OutputNtpStatus, true, 0, 1, 0,true),
    ("Windows Search 状態", OutputWSearchDetail_E, true, 1, 2, 4,false)        //マニアックなので標準使用停止
        };
*/
        // ============================
        // 2. cfg にマスターテーブル行があるか？
        // ============================
        if (CfgTemp.MasterLines != null && CfgTemp.MasterLines.Count > 0)
        {
            try
            {
                mappings = ParseMasterLines(CfgTemp.MasterLines,
                    defaultMap);
            }
            catch (Exception ex)
            {
                Console.WriteLine("[InitializeMethodMaps] cfg パース失敗: " + ex.Message);
                mappings = defaultMap; // フォールバック
            }
        }
        else
        {
            // cfg にマスターテーブルが無い → defaultMap を使用
            mappings = defaultMap;
        }

        // ============================
        // 3. sb_data の初期化（Ver1.9 と同じ）
        // ============================
        MaxMethod = (byte)mappings.Length;

        sb_data = new ConcurrentDictionary<int, string>();
        for (int i = 0; i < MaxMethod; i++)
            sb_data[i] = "";
    }

    //新.cfg読み込みパース、変数(クラス)格納
    static void LoadNewCfg()
    {
        string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ObservePC.cfg");

        if (!File.Exists(path))
            return;

        try
        {
            string[] lines = File.ReadAllLines(path, Encoding.UTF8);

            foreach (string raw in lines)
            {
                string line = raw.Trim();

                // 空行・コメント行はスキップ
                if (string.IsNullOrEmpty(line) || line.StartsWith("#"))
                    continue;

                // -------------------------
                // Engine = S/F/M
                // -------------------------
                if (line.StartsWith("Engine", StringComparison.OrdinalIgnoreCase))
                {
                    try
                    {
                        var parts = line.Split('=');
                        if (parts.Length == 2)
                        {
                            string val = parts[1].Trim().ToUpper();
                            if (val == "S" || val == "F" || val == "M")
                                CfgTemp.Engine = val;
                        }
                    }
                    catch { /* 無視して続行 */ }
                    continue;
                }

                // -------------------------
                // ExpertMode = true/false
                // -------------------------
                if (line.StartsWith("ExpertMode", StringComparison.OrdinalIgnoreCase))
                {
                    try
                    {
                        var parts = line.Split('=');
                        if (parts.Length == 2)
                        {
                            bool ex;
                            if (bool.TryParse(parts[1].Trim(), out ex))
                                CfgTemp.ExpertMode = ex;
                        }
                    }
                    catch { }
                    continue;
                }

                // -------------------------
                // MaxData = 10-200
                // -------------------------
                if (line.StartsWith("MaxData", StringComparison.OrdinalIgnoreCase))
                {
                    try
                    {
                        var parts = line.Split('=');
                        if (parts.Length == 2)
                        {
                            byte md;
                            if (byte.TryParse(parts[1].Trim(), out md))
                                CfgTemp.MaxData = md;
                        }
                    }
                    catch { }
                    continue;
                }

                // -------------------------
                // マスターテーブル行（カンマ区切り）
                // -------------------------
                if (line.Contains(","))
                {
                    try
                    {
                        // 最低7項目必要
                        var parts = line.Split(',');
                        if (parts.Length >= 7)
                        {
                            // 形式が正しければそのまま格納
                            CfgTemp.MasterLines.Add(line);
                        }
                    }
                    catch
                    {
                        // 1行だけ壊れていても全体は続行
                    }

                    continue;
                }

                // 上記に該当しない行は無視
            }
        }
        catch (Exception ex)
        {
            // cfg 全体が壊れていても ObservePC は動作継続
            Console.WriteLine("[LoadNewCfg] 読み込みエラー: " + ex.Message);
        }
    }



    //取得エンジンと最大取得件数、ExpertModeの設定
    static void ApplyCfg()
    {
        try
        {
            // ============================
            // MaxData の反映
            // ============================
            if (CfgTemp.MaxData.HasValue)
            {
                // 1-200 の範囲に収まるかチェック
                byte md = CfgTemp.MaxData.Value;
                if (md >= 1 && md <= 200)   // 一応広めに許容
                    MaxDataOutput = md;
            }

            // ============================
            // ExpertMode の反映
            // ============================
            if (CfgTemp.ExpertMode.HasValue)
            {
                expertMode = CfgTemp.ExpertMode.Value;

                //---Ver2.1で書き換え
                // ExpertMode = true の場合は MaxData を拡張値に
                //if (expertMode)  MaxDataOutput = MaxDataOutput_E;     //Ver2.0のバグの原因


                //---Ver2.1ここまで

            }

            // ============================
            // Engine の反映
            // ============================
            if (!string.IsNullOrEmpty(CfgTemp.Engine))
            {
                string eng = CfgTemp.Engine.ToUpper();

                switch (eng)
                {
                    case "S":
                        SafetyMode = true;
                        FastMode = false;
                        break;

                    case "F":
                        FastMode = true;
                        SafetyMode = false;
                        expertMode = true;          // Ver1.9 と同じ挙動
                        MaxDataOutput = MaxDataOutput_E;
                        break;

                    case "M":
                        // 多段並列（標準）
                        // 特にフラグは不要（Ver1.9 のデフォルト動作）
                        break;
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("[ApplyCfg] 反映エラー: " + ex.Message);
        }
    }



    private static (string Comment,
                    Func<string> Method,
                    bool IsExtended,byte WmiLevel,byte LoadLevel,byte ConflictGroup,bool Ran_Sw)[] ParseMasterLines(
                        List<string> lines,
                        (string, Func<string>, bool, byte, byte, byte, bool)[] defaultMap)
    {
        var list = new List<(string, Func<string>, bool, byte, byte, byte, bool)>();

        foreach (string raw in lines)
        {
            string line = raw.Trim();
            if (string.IsNullOrEmpty(line) || line.StartsWith("#"))
                continue;

            try
            {
                var parts = line.Split(',');
                if (parts.Length < 7)
                    continue;

                string comment = parts[0].Trim();
                string methodName = parts[1].Trim();

                bool isExt = false;
                byte wmi = 0;
                byte load = 1;
                byte conflict = 0;
                bool ran = true;

                bool.TryParse(parts[2].Trim(), out isExt);
                byte.TryParse(parts[3].Trim(), out wmi);
                byte.TryParse(parts[4].Trim(), out load);
                byte.TryParse(parts[5].Trim(), out conflict);
                bool.TryParse(parts[6].Trim(), out ran);

                Func<string> method = null;

                for (int i = 0; i < defaultMap.Length; i++)
                {
                    if (defaultMap[i].Item2.Method.Name.Equals(methodName, StringComparison.OrdinalIgnoreCase))
                    {
                        method = defaultMap[i].Item2;
                        break;
                    }
                }

                if (method == null)
                    continue;

                list.Add((comment, method, isExt, wmi, load, conflict, ran));
            }
            catch
            {
                continue;
            }
        }

        return list.ToArray();
    }







    // --- 各情報取得メソッド ---
    
    // Win32 API (EnumDisplayDevices) を使用してGPUと接続モニター情報を取得
    static string OutputDisplayInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ディスプレイ情報（EnumDisplayDevicesより）：");

        try
        {
            // ============================
            // 1. 接続ディスプレイ台数のカウント
            // ============================
            int displayCount = 0;
            DISPLAY_DEVICE ddCount = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };

            for (uint di = 0; EnumDisplayDevices(null, di, ref ddCount, 0); di++)
            {
                // 0x00000001 が「デスクトップに接続されている」フラグ
                if ((ddCount.StateFlags & 0x00000001) != 0)
                {
                    displayCount++;
                }
            }
            sb.AppendLine($"接続ディスプレイ数: {displayCount}台");
            sb.AppendLine();

            // ============================
            // 2. GPU と 1台目のモニター情報
            // ============================
            DISPLAY_DEVICE gpu = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };

            for (uint i = 0; EnumDisplayDevices(null, i, ref gpu, 0); i++)
            {
                sb.AppendLine($"GPU {i}: {gpu.DeviceString}");

                DISPLAY_DEVICE monitor = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };

                if (EnumDisplayDevices(gpu.DeviceName, 0, ref monitor, 0))
                {
                    sb.AppendLine($"  -> モニター: {monitor.DeviceString}");
                    sb.AppendLine($"     PnP名: {monitor.DeviceName}");

                    // ============================
                    // 3. 色深度の取得（取れたら表示）
                    // ============================
                    try
                    {
                        DEVMODE dm = new DEVMODE();
                        dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));

                        if (EnumDisplaySettings(monitor.DeviceName, ENUM_CURRENT_SETTINGS, ref dm))
                        {
                            if (dm.dmBitsPerPel > 0 && dm.dmBitsPerPel <= 48)
                                sb.AppendLine($"     色深度: {dm.dmBitsPerPel}bit");
                        }
                    }
                    catch { /* 色深度は取得できなくても問題なし */ }

                    // ============================
                    // 4. HDR ON/OFF の取得（1台目のみ）
                    // ============================
                    try
                    {
                        var hdrInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
                        hdrInfo.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
                        hdrInfo.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
                        hdrInfo.header.adapterId = new LUID();
                        hdrInfo.header.id = 0;

                        int status = DisplayConfigGetDeviceInfo(ref hdrInfo);

                        if (status == 0)
                        {
                            if (hdrInfo.advancedColorSupported == 0)
                                sb.AppendLine("     HDR: 未対応");
                            else
                                sb.AppendLine($"     HDR: {(hdrInfo.advancedColorEnabled != 0 ? "有効" : "無効")}");
                        }
                        else
                        {
                            sb.AppendLine("     HDR: 取得不可");
                        }
                    }
                    catch
                    {
                        sb.AppendLine("     HDR: 取得不可");
                    }

                    Thread.Sleep(50);
                    break; // 1台目だけで十分
                }

                gpu = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[OutputDisplayInfo] エラー: {ex.Message}");
            return "";
        }

        return sb.ToString();
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct DEVMODE
    {
        private const int CCHDEVICENAME = 32;
        private const int CCHFORMNAME = 32;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
        public string dmDeviceName;

        public short dmSpecVersion;
        public short dmDriverVersion;
        public short dmSize;
        public short dmDriverExtra;
        public int dmFields;

        public int dmPositionX;
        public int dmPositionY;
        public int dmDisplayOrientation;
        public int dmDisplayFixedOutput;

        public short dmColor;
        public short dmDuplex;
        public short dmYResolution;
        public short dmTTOption;
        public short dmCollate;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
        public string dmFormName;

        public short dmLogPixels;
        public int dmBitsPerPel;        // ★ 色深度（8 / 10 / 12 / 32 など）
        public int dmPelsWidth;
        public int dmPelsHeight;

        public int dmDisplayFlags;
        public int dmDisplayFrequency;

        public int dmICMMethod;
        public int dmICMIntent;
        public int dmMediaType;
        public int dmDitherType;
        public int dmReserved1;
        public int dmReserved2;

        public int dmPanningWidth;
        public int dmPanningHeight;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool EnumDisplaySettings(
    string deviceName,
    int modeNum,
    ref DEVMODE devMode);

    public const int ENUM_CURRENT_SETTINGS = -1;

    public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : int
    {
        DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 0x0000000D
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct LUID
    {
        public uint LowPart;
        public int HighPart;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DISPLAYCONFIG_DEVICE_INFO_HEADER
    {
        public DISPLAYCONFIG_DEVICE_INFO_TYPE type;
        public int size;
        public LUID adapterId;
        public uint id;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO
    {
        public DISPLAYCONFIG_DEVICE_INFO_HEADER header;

        public uint advancedColorSupported;   // 1 = HDR/WCG対応
        public uint advancedColorEnabled;     // 1 = HDR有効
        public uint wideColorEnforced;
        public uint advancedColorForceDisabled;
    }

    [DllImport("user32.dll")]
    public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO requestPacket);

    /*
        // Win32 API (EnumDisplayDevices) を使用してGPUと接続モニター情報を取得
        static string OutputDisplayInfo()
        {
            var sb = new StringBuilder();
            sb.AppendLine("?? ディスプレイ情報（EnumDisplayDevicesより）：");

            try
            {
                DISPLAY_DEVICE gpu = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };

                for (uint i = 0; EnumDisplayDevices(null, i, ref gpu, 0); i++)
                {
                    sb.AppendLine($"GPU {i}: {gpu.DeviceString}");

                    DISPLAY_DEVICE monitor = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };

                    if (EnumDisplayDevices(gpu.DeviceName, 0, ref monitor, 0))
                    {
                        sb.AppendLine($"  -> モニター: {monitor.DeviceString}");
                        sb.AppendLine($"     PnP名: {monitor.DeviceName}");
                        //await Task.Delay(1000);
                        Thread.Sleep(50);
                        break;
                    }

                    gpu = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[OutputDisplayInfo] エラー: {ex.Message}");
                return "";
                //sb.AppendLine($"[OutputDisplayInfo] エラー: {ex.Message}");
            }

            return sb.ToString();
        }
    */
    // WMI (Win32_OperatingSystem) からOS情報を取得

    // OS情報
    static string OutputOSInfo()
    {
        var sb = new StringBuilder();
        // 共通ヘッダー
        sb.AppendLine("?? OS情報：");
        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT Caption, Version, OSArchitecture, CSDVersion FROM Win32_OperatingSystem"))
            {
                foreach (var os in searcher.Get())
                {
                    sb.AppendLine($"[OS] {os["Caption"]} {os["Version"]} ({os["OSArchitecture"]}) {os["CSDVersion"]}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[OS情報取得失敗] {ex.Message}");
            return "";
            //sb.AppendLine($"[OS情報取得失敗] {ex.Message}");
        }
        return sb.ToString();
    }


    // WMI (Win32_Processor, Win32_ComputerSystem, Win32_PhysicalMemory) からCPU/RAM情報を取得

    static string OutputSystemInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? CPUとメモリー情報：");

        // CPU情報
        var cpuSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Processor");
        foreach (ManagementObject cpu in cpuSearcher.Get())
        {
            sb.AppendLine($"CPU: {cpu["Name"]}");
            sb.AppendLine($"コア数: {cpu["NumberOfCores"]}, スレッド数: {cpu["NumberOfLogicalProcessors"]}");
        }

        // RAM合計容量
        var memSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem");
        foreach (ManagementObject mem in memSearcher.Get())
        {
            // TotalPhysicalMemory はバイト単位なので、MBに変換
            ulong totalMemory = Convert.ToUInt64(mem["TotalPhysicalMemory"]);
            sb.AppendLine($"RAM合計: {totalMemory / (1024 * 1024)} MB");
        }

        // RAM速度と種類 (スロット別)
        var ramSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMemory");
        foreach (ManagementObject ram in ramSearcher.Get())
        {
            // MemoryType (24=DDR3, 26=DDR4, 34=DDR5 など。WMIの規格値を使用)
            string memoryType = GetMemoryType(Convert.ToInt32(ram["MemoryType"]));
            ulong sizeMB = Convert.ToUInt64(ram["Capacity"]) / (1024 * 1024);
            sb.AppendLine($"RAM Slot: {ram["DeviceLocator"]}, Speed: {ram["Speed"]} MHz, Type: {memoryType}, Size: {sizeMB} MB");
        }
        return sb.ToString();
    }

    // WMIのMemoryTypeコードをDDR世代に変換 (C# 8.0以降のswitch式を使用 以前でも.csprojに追記で可能な場合有り)

    static string GetMemoryType(int type)
    {
        return type switch
        {
            24 => "DDR3",
            26 => "DDR4",
            34 => "DDR5",
            _ => "Unknown" // その他の値はUnknownとする
        };
    }


    // WMI (Win32_LogicalDisk) からストレージ情報を取得

    static string OutputDiskInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ストレージ情報 (容量と空き容量)：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_LogicalDisk");
            foreach (ManagementObject disk in searcher.Get())
            {
                string deviceId = Clean(disk["DeviceID"]?.ToString()).ToUpper();
                string volumeName = Clean(disk["VolumeName"]?.ToString());

                int driveType = Convert.ToInt32(disk["DriveType"]);
                string typeLabel = driveType switch
                {
                    2 => "リムーバブル",
                    3 => "ローカル",
                    4 => "ネットワーク",
                    5 => "CD/DVD",
                    6 => "RAMディスク",
                    _ => "その他"
                };

                ulong totalBytes = disk["Size"] != null ? Convert.ToUInt64(disk["Size"]) : 0;
                ulong freeBytes = disk["FreeSpace"] != null ? Convert.ToUInt64(disk["FreeSpace"]) : 0;

                string totalGB = (totalBytes / (1024 * 1024 * 1024)).ToString();
                string freeGB = (freeBytes / (1024 * 1024 * 1024)).ToString();

                sb.AppendLine($"ドライブ {deviceId} - 合計: {totalGB} GB, 空き: {freeGB} GB, ボリューム名: {volumeName}, 種別: {typeLabel}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[OutputDiskInfo] エラー: {ex.Message}");
            return "";
            //sb.AppendLine($"[OutputDiskInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }


    static string Clean(string input) => input?.Replace("\0", "").Trim() ?? "";



    //接続デバイス　主にUSB
    static string OutputDeviceInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? 接続されているデバイス一覧（分類・最大" + MaxDataOutput + "件）：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity");
            var allDevices = new List<string>();
            var avDevices = new List<string>();

            foreach (ManagementObject device in searcher.Get())
            {
                string name = device["Name"]?.ToString();
                if (string.IsNullOrEmpty(name)) continue;

                if (name.IndexOf("Bus", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Component", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Driver", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Service", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Controller", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    continue;
                }

                if (name.IndexOf("Microphone", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Webcam", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Camera", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Audio", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("DAC", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Capture", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Video", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    avDevices.Add(name);
                }
                else if (
                    name.IndexOf("Hub", StringComparison.OrdinalIgnoreCase) < 0 &&
                    (name.IndexOf("USB", StringComparison.OrdinalIgnoreCase) >= 0 ||
                     name.IndexOf("Bluetooth", StringComparison.OrdinalIgnoreCase) >= 0 ||
                     name.IndexOf("Printer", StringComparison.OrdinalIgnoreCase) >= 0 ||
                     name.IndexOf("Display", StringComparison.OrdinalIgnoreCase) >= 0))
                {
                    bool isHub =
                        name.IndexOf("Hub", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.Contains("ハブ") ||
                        name.Contains("ルート ハブ") ||
                        name.Contains("Composite") ||
                        name.Contains("SuperSpeed USB ハブ");

                    bool isUsefulUSB =
                        name.IndexOf("Input", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Keyboard", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Mouse", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Storage", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Logitech", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("DAC", StringComparison.OrdinalIgnoreCase) >= 0;

                    if (!isHub && (name.IndexOf("USB", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   name.IndexOf("Bluetooth", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   name.IndexOf("Printer", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   name.IndexOf("Display", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   isUsefulUSB))
                    {
                        allDevices.Add(name);
                    }
                }
            }

            sb.AppendLine("?? 録音・映像機器:");
            foreach (var dev in avDevices.Take(MaxDataOutput))
            {
                sb.AppendLine("- " + dev);
            }

            sb.AppendLine("??? その他の接続デバイス:");
            foreach (var dev in allDevices.Take(MaxDataOutput))
            {
                sb.AppendLine("- " + dev);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[OutputDeviceInfo] エラー: {ex.Message}");
            return "";
            //sb.AppendLine($"[OutputDeviceInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }


    // WMI (Win32_VideoController) からGPU情報を取得

    static string OutputGPUInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? GPU情報：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_VideoController");

            foreach (ManagementObject gpu in searcher.Get())
            {
                sb.AppendLine($"名前: {gpu["Name"]}");
                sb.AppendLine($"ドライババージョン: {gpu["DriverVersion"]}");

                // AdapterRAM (VRAM) は4GB以上でWMIの型変換に問題が出ることがあるため、チェックを追加
                if (gpu["AdapterRAM"] is ulong adapterRam)
                {
                    sb.AppendLine($"VRAM: {adapterRam / (1024 * 1024)} MB");
                }
                else
                {
                    sb.AppendLine("VRAM: (取得不可 or 4GB超過によるWMIの制限) MB");
                }

                // 現在の解像度情報
                if (gpu["CurrentHorizontalResolution"] != null && gpu["CurrentVerticalResolution"] != null)
                {
                    sb.AppendLine($"解像度: {gpu["CurrentHorizontalResolution"]} x {gpu["CurrentVerticalResolution"]}");
                }

                sb.AppendLine();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[OutputGPUInfo] エラー: {ex.Message}");
            return "";
            //sb.AppendLine($"[OutputGPUInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    // Windowsイベントログから最近のエラーと警告を取得し、ノイズを除去

    static string OutputEventLog()
    {
        byte LogOutput = (byte)(MaxDataOutput + 5);             //最大件数を+5
        //if (expertMode) LogOutput = (byte)(LogOutput + 10);      //-E時に最大件数を+10
        var sb = new StringBuilder();
        sb.AppendLine("?? 最近のシステムイベントログ（Error/Warningのみ、10016除外 最大" + LogOutput + "件）:");

        try
        {
            using (EventLog systemLog = new EventLog("System"))
            {
                int count = 0;
                for (int i = systemLog.Entries.Count - 1; i >= 0 && count < LogOutput; i--)
                {
                    EventLogEntry entry = systemLog.Entries[i];

                    if ((entry.EntryType == EventLogEntryType.Error || entry.EntryType == EventLogEntryType.Warning)
                        && entry.Source != "DCOM" && entry.EventID != 10016)
                    {
                        sb.AppendLine($"[{entry.TimeGenerated:yyyy-MM-dd HH:mm:ss}] {entry.EntryType} - Source: {entry.Source}, EventID: {entry.EventID}");

                        string message = entry.Message.Replace('\n', ' ').Replace('\r', ' ');
                        sb.AppendLine($"    -> Message: {message.Substring(0, Math.Min(message.Length, 150))}...");

                        count++;
                    }
                }

                if (count == 0)
                {
                    sb.AppendLine("-> 過去のログに重大なError/Warningは検出されませんでした。");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[OutputEventLog] エラー: {ex.Message}");
            //sb.AppendLine($"[OutputEventLog] エラー: {ex.Message}");
            return "";
        }

        return sb.ToString();
    }

    // WMI (Win32_BaseBoard) からマザーボード情報を取得

    // マザーボード情報
    static string OutputMotherboardInfo()
    {
        var sb = new StringBuilder();
        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT Manufacturer, Product, SerialNumber FROM Win32_BaseBoard"))
            {
                foreach (var board in searcher.Get())
                {
                    sb.AppendLine($"[マザーボード] {board["Manufacturer"]} {board["Product"]} SN:{board["SerialNumber"]}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[マザーボード情報取得失敗] {ex.Message}");
            //sb.AppendLine($"[マザーボード情報取得失敗] {ex.Message}");
            return "";
        }
        return sb.ToString();
    }


    // WMI (Win32_OperatingSystem) からPCの起動時刻と稼働時間を取得

    static string OutputUptimeInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine();
        sb.AppendLine("?? 時間情報：");

        var searcher = new ManagementObjectSearcher("SELECT LastBootUpTime FROM Win32_OperatingSystem");
        foreach (ManagementObject os in searcher.Get())
        {
            string lastBootUp = os["LastBootUpTime"]?.ToString();
            if (!string.IsNullOrEmpty(lastBootUp))
            {
                // WMIの日付形式をC#のDateTimeに変換
                DateTime bootTime = ManagementDateTimeConverter.ToDateTime(lastBootUp);
                // TimeSpan uptime = DateTime.Now - bootTime; // 稼働時間は省略 (コメントアウト)

                sb.AppendLine($"PC起動時刻: {bootTime:yyyy-MM-dd HH:mm:ss}");
            }
        }
        return sb.ToString();
    }


    // 環境変数からPC名を取得

    // コンピュータ名、ドメイン、ユーザーなど
    static string OutputMachineInfo()
    {
        var sb = new StringBuilder();
        try
        {
            sb.AppendLine($"[マシン名] {Environment.MachineName}");
            sb.AppendLine($"[ユーザー名] {Environment.UserName}");
            sb.AppendLine($"[ドメイン] {Environment.UserDomainName}");
            sb.AppendLine($"[アーキテクチャ] {(Environment.Is64BitOperatingSystem ? "64bit" : "32bit")}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[マシン情報取得失敗] {ex.Message}");
            //sb.AppendLine($"[マシン情報取得失敗] {ex.Message}");
            return "";
        }
        return sb.ToString();
    }

    //スタートアップ一覧取得
    static string OutputStartupApps()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? スタートアップアプリ一覧：");

        try
        {
            string registryPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(registryPath))
            {
                if (key != null)
                {
                    foreach (string name in key.GetValueNames())
                    {
                        string value = key.GetValue(name)?.ToString();
                        sb.AppendLine($"- {name}: {value}");
                    }
                }
                else
                {
                    //sb.AppendLine("-> スタートアップ情報が取得できませんでした。");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[OutputStartupApps] エラー: {ex.Message}");
            //sb.AppendLine($"[OutputStartupApps] エラー: {ex.Message}");
            return "";
        }

        return sb.ToString();
    }



    //Windows Update履歴取得
    static string OutputUpdateHistory()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? Windows Update履歴（KB番号とインストール日）：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_QuickFixEngineering");

            // 一旦リストに格納してソート
            var updates = new List<(string HotFixID, DateTime? InstalledOn)>();

            foreach (ManagementObject update in searcher.Get())
            {
                string hotfixId = update["HotFixID"]?.ToString();
                string installedOnStr = update["InstalledOn"]?.ToString();

                DateTime? installedOn = null;
                if (DateTime.TryParse(installedOnStr, out DateTime dt))
                    installedOn = dt;

                updates.Add((hotfixId, installedOn));
            }

            // 日付降順（新しい順）でソート
            var sorted = updates
                .OrderByDescending(u => u.InstalledOn ?? DateTime.MinValue)
                .ToList();

            // 出力
            foreach (var u in sorted)
            {
                string dateStr = u.InstalledOn?.ToString("yyyy-MM-dd") ?? "不明";
                sb.AppendLine($"- {u.HotFixID} (Installed: {dateStr})");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[OutputUpdateHistory] エラー: {ex.Message}");
            //sb.AppendLine($"[OutputUpdateHistory] エラー: {ex.Message}");
            return "";
        }

        return sb.ToString();
    }


    /*
    static string OutputUpdateHistory()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? Windows Update履歴（KB番号とインストール日）：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_QuickFixEngineering");
            foreach (ManagementObject update in searcher.Get())
            {
                string hotfixId = update["HotFixID"]?.ToString();
                string installedOn = update["InstalledOn"]?.ToString();
                sb.AppendLine($"- {hotfixId} (Installed: {installedOn})");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputUpdateHistory] エラー: {ex.Message}");
            return "";
        }

        return sb.ToString();
    }
    */

    // NIC構成取得（Ver1.4+ 製造元付き・MAC照合方式）
    static string OutputNetworkInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ネットワーク構成（System.Net + WMIより）：");

        // WMIから MAC → 製造元 の辞書を作成
        var vendorMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        try
        {
            var wmiSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapter WHERE PhysicalAdapter = TRUE");
            foreach (ManagementObject nic in wmiSearcher.Get())
            {
                string mac = nic["MACAddress"]?.ToString();
                string manufacturer = nic["Manufacturer"]?.ToString();
                if (!string.IsNullOrEmpty(mac) && !string.IsNullOrEmpty(manufacturer))
                {
                    string normalizedMac = mac.Replace(":", "").Replace("-", "").ToUpperInvariant();
                    vendorMap[normalizedMac] = manufacturer;
                }
            }
        }
        catch
        {
            Console.WriteLine("-> NIC製造元情報の取得に失敗しました（WMIアクセス不可）");
            //sb.AppendLine("-> NIC製造元情報の取得に失敗しました（WMIアクセス不可）");
            return "";
        }

        // System.Net.NetworkInformation でNIC状態を取得
        var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();

        foreach (var ni in interfaces)
        {
            if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
            var props = ni.GetIPProperties();
            var ipList = props.UnicastAddresses.Select(ip => ip.Address.ToString()).ToList();
            var dnsList = props.DnsAddresses.Select(dns => dns.ToString()).ToList();
            var gatewayList = props.GatewayAddresses.Select(g => g.Address.ToString()).ToList();

            string mac = ni.GetPhysicalAddress().ToString().ToUpperInvariant();

            sb.AppendLine($"NIC: {ni.Name}");

            if (!string.IsNullOrEmpty(mac) && vendorMap.TryGetValue(mac, out string vendor))
            {
                sb.AppendLine($"  製造元: {vendor}");
            }
            else
            {
                sb.AppendLine($"  製造元: (取得不可 or WMI照合失敗)");
            }

            sb.AppendLine($"  種別: {ni.NetworkInterfaceType}");
            sb.AppendLine($"  状態: {ni.OperationalStatus}");
            sb.AppendLine($"  MAC: {mac}");
            sb.AppendLine($"  リンク速度: {ni.Speed / 1_000_000} Mbps");
            sb.AppendLine($"  IP: {string.Join(", ", ipList)}");
            sb.AppendLine($"  DNS: {string.Join(", ", dnsList)}");
            sb.AppendLine($"  Gateway: {string.Join(", ", gatewayList)}");
        }
        return sb.ToString();
    }


    static string OutputResidentProcesses()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? サードパーティ製プロセス一覧（常駐候補）：");

        var windowsPaths = new[]
        {
           "C:\\Windows",
           "C:\\Program Files\\WindowsApps",
           "C:\\Program Files\\Microsoft Visual Studio",
           //"C:\\Users\\<USERNAME>\\source\\repos", // 自作実行ファイル
           //"C:\\Users\\<USERNAME>\\AppData\\Local\\Microsoft", // BingSvcなど
           // 必要に応じて追加
        };
        var processes = Process.GetProcesses();

        // グループ化用の辞書：フォルダパス → プロセス一覧
        var groups = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

        foreach (var proc in processes)
        {
            try
            {
                string name = proc.ProcessName;
                string path = proc.MainModule?.FileName ?? null;

                if (string.IsNullOrEmpty(path)) continue;

                // Windows標準プロセスを除外
                if (windowsPaths.Any(p => path.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
                    continue;

                string folder = Path.GetDirectoryName(path) ?? "(不明なフォルダ)";
                string entry = $"- {name}: {path}";

                if (!groups.ContainsKey(folder))
                    groups[folder] = new List<string>();

                groups[folder].Add(entry);
            }
            catch
            {
                // アクセス拒否などは無視
            }
        }

        // 出力（フォルダ単位でグループ化）
        foreach (var kv in groups.OrderBy(k => k.Key))
        {
            sb.AppendLine($"\n■ {kv.Key}：");
            foreach (var line in kv.Value)
            {
                sb.AppendLine(line);
            }
        }
        return sb.ToString();
    }
    //セキュリティソフトの常駐状況
    static string OutputSecurityStatus()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? セキュリティソフトの常駐状況（推定）：");

        try
        {
            var searcher = new ManagementObjectSearcher(@"root\SecurityCenter2", "SELECT * FROM AntiVirusProduct");
            foreach (ManagementObject av in searcher.Get())
            {
                sb.AppendLine($"- 製品名: {av["displayName"]}");
                sb.AppendLine($"  状態: {av["productState"]}"); // 数値コード（詳細はMS仕様）
            }
        }
        catch
        {
            Console.WriteLine("-> セキュリティセンター情報が取得できませんでした。");
            //sb.AppendLine("-> セキュリティセンター情報が取得できませんでした。");
            return "";
        }

        // サードパーティ製の推定（プロセス名から）
        var knownAV = new[] { "eset", "avast", "norton", "mcafee", "kaspersky", "trend", "bitdefender" };
        var processes = Process.GetProcesses();

        var detected = processes
            .Where(p => knownAV.Any(av => p.ProcessName.ToLower().Contains(av)))
            .Select(p => p.ProcessName)
            .Distinct()
            .ToList();

        if (detected.Count > 0)
        {
            sb.AppendLine("-> サードパーティ製セキュリティソフトの常駐候補：");
            foreach (var name in detected)
            {
                sb.AppendLine($"- {name}");
            }
        }
        else
        {
            sb.AppendLine("-> サードパーティ製セキュリティソフトは検出されませんでした。");
        }
        return sb.ToString();
    }

    // BIOS情報
    static string OutputBIOSInfo()
    {
        var sb = new StringBuilder();
        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT Manufacturer, SMBIOSBIOSVersion, ReleaseDate FROM Win32_BIOS"))
            {
                foreach (var bios in searcher.Get())
                {
                    sb.AppendLine($"[BIOS] {bios["Manufacturer"]} {bios["SMBIOSBIOSVersion"]} ({bios["ReleaseDate"]})");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[BIOS情報取得失敗] {ex.Message}");
            //sb.AppendLine($"[BIOS情報取得失敗] {ex.Message}");
            return "";
        }
        return sb.ToString();
    }

    //異常ドライバー検出 収集ロジック
    static List<(string Name, string Status, string Signature, int ErrorCode)> CollectDriverIssues()
    {

        var issues = new List<(string, string, string, int)>();

        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity"))
            {
                foreach (ManagementObject obj in searcher.Get())
                {
                    string name = obj["Name"]?.ToString() ?? "（名称不明）";
                    int errorCode = Convert.ToInt32(obj["ConfigManagerErrorCode"] ?? 0);

                    if (name.ToLower().Contains("unknown") || errorCode != 0)
                    {
                        issues.Add((name, "不明またはエラー", "不明", errorCode));
                    }
                }
            }

            using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPSignedDriver"))
            {
                foreach (ManagementObject obj in searcher.Get())
                {
                    string name = obj["DeviceName"]?.ToString() ?? "（名称不明）";
                    bool isSigned = Convert.ToBoolean(obj["IsSigned"] ?? true);

                    if (!isSigned)
                    {
                        issues.Add((name, "未署名", "なし", 0));
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ドライバー情報の取得中にエラーが発生しました");
            //issues.Add(($"（ドライバー情報取得失敗: {ex.Message}）", "取得失敗", "不明", -1));
    
        }

        return issues;
    }

    //異常ドライバー検出 出力
    static string OutputDriverIssues()
    {
        var sb = new StringBuilder();
        var issues = CollectDriverIssues();
        sb.AppendLine("?? 異常ドライバー検出:");
        if (issues.Count == 0) return sb.ToString();
        try
        {
            

            if (!expertMode)
                sb.AppendLine("※ 不明なデバイスや未署名ドライバーを検出しました。詳細は -E モードで確認できます。");

            foreach (var issue in issues)
            {
                if (expertMode)
                {
                    sb.AppendLine($"- {issue.Name}");
                    sb.AppendLine($"  状態: {issue.Status}");
                    sb.AppendLine($"  署名: {issue.Signature}");
                    sb.AppendLine($"  エラーコード: {issue.ErrorCode}");
                }
                else
                {
                    sb.AppendLine($"- {issue.Name}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ドライバー情報の取得中にエラーが発生しました");
            return "";
            //sb.AppendLine("?? 異常ドライバー検出:");
            //sb.AppendLine("※ ドライバー情報の取得中にエラーが発生しました。");
            //sb.AppendLine($"エラー内容: {ex.Message}");
        }
        return sb.ToString();
    }

    // タスクスケジューラ登録一覧（不正ソフトが居るかも）
    // ============================================================================
    // ObservePC: タスクスケジューラ登録一覧（XML解析 + LIST/Vモード + 状態＆トリガー型）
    // ============================================================================

    static string GetTaskSchedulerInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? タスクスケジューラ登録一覧（整形済・フィルタ＋状態＋トリガー型対応）：");

        try
        {
            string xmlOutput = RunCmd("schtasks /query /xml ONE");

            if (!string.IsNullOrWhiteSpace(xmlOutput) && xmlOutput.Contains("<Task"))
            {
                sb.AppendLine(ParseTasksFromXml(xmlOutput));
            }
            else
            {
                sb.AppendLine("-> XML解析不可。LIST/Vモードで再試行します。");
                sb.AppendLine(GetTaskSchedulerListFallback());
            }
        }
        catch
        {
            Console.WriteLine("-> XML解析失敗。LIST/Vモードで再試行します。");
            //sb.AppendLine("-> XML解析失敗。LIST/Vモードで再試行します。");
            sb.AppendLine(GetTaskSchedulerListFallback());
        }

        return sb.ToString();
    }

    // ============================================================================
    // XML解析ロジック
    // ============================================================================
    static string ParseTasksFromXml(string xml)
    {
        var result = new StringBuilder();
        int count = 1;
        var matches = Regex.Split(xml, @"(?=<Task )");

        foreach (var block in matches)
        {
            if (!block.Contains("<Task")) continue;

            string name = ExtractXmlTag(block, "URI");
            string author = ExtractXmlTag(block, "Author");
            string cmd = ExtractXmlTag(block, "Command");
            string enabled = ExtractXmlTag(block, "Enabled");
            string triggerType = ExtractTriggerType(block);
            string start = ExtractXmlTag(block, "StartBoundary");

            string trigger = $"{triggerType} {(string.IsNullOrEmpty(start) ? "" : "開始: " + start)}";

            if (string.IsNullOrWhiteSpace(name)) continue;
            if (IsSystemTask(name, author, cmd)) continue;

            string status = enabled == "false" ? "無効" : "有効";

            result.AppendLine($"■ タスク {count}: {name}");
            result.AppendLine($"  状態: {status}");
            result.AppendLine($"  登録者: {author}");
            result.AppendLine($"  トリガー: {trigger}");
            result.AppendLine($"  実行コマンド: {cmd}");
            result.AppendLine();
            count++;
        }

        if (count == 1)
            result.AppendLine("-> 登録されたタスクが見つかりませんでした。");

        return result.ToString();
    }

    static string ExtractXmlTag(string text, string tag)
    {
        var m = Regex.Match(text, $"<{tag}>(.*?)</{tag}>", RegexOptions.Singleline);
        return m.Success ? m.Groups[1].Value.Trim() : "";
    }

    // トリガー型（LogonTrigger, CalendarTrigger など）を抽出
    static string ExtractTriggerType(string xmlBlock)
    {
        var m = Regex.Match(xmlBlock, @"<Triggers>.*?<(\w+?)>", RegexOptions.Singleline);
        return m.Success ? m.Groups[1].Value.Trim() : "(取得不可)";
    }

    // ============================================================================
    // LIST/Vフォールバックモード
    // ============================================================================
    static string GetTaskSchedulerListFallback()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? タスクスケジューラ登録一覧（LIST/Vモード・フィルタ適用）：");

        try
        {
            var output = RunCmd("schtasks /query /fo LIST /v");
            if (string.IsNullOrWhiteSpace(output))
            {
                sb.AppendLine("-> タスク情報の取得に失敗しました。");
                return sb.ToString();
            }

            var blocks = Regex.Split(output, @"\r?\n\r?\n").Where(b => b.Contains("タスク名:")).ToList();
            int count = 1;

            foreach (var block in blocks)
            {
                var (name, author, trigger, cmd, state) = ParseTaskBlock(block);

                if (IsSystemTask(name, author, cmd))
                    continue;

                if (name == "(取得不可)" && cmd == "(取得不可)")
                    continue;

                sb.AppendLine($"■ タスク {count}: {name}");
                sb.AppendLine($"  状態: {state}");
                sb.AppendLine($"  登録者: {author}");
                sb.AppendLine($"  トリガー: {trigger}");
                sb.AppendLine($"  実行コマンド: {cmd}");
                sb.AppendLine();
                count++;
            }

            if (count == 1)
                sb.AppendLine("-> 登録されたタスクが見つかりませんでした。");
        }
        catch (Exception ex)
        {
            Console.WriteLine("-> LIST/Vモード解析エラー: " + ex.Message);
            //sb.AppendLine("-> LIST/Vモード解析エラー: " + ex.Message);
            return "";
        }

        return sb.ToString();
    }

    static (string Name, string Author, string Trigger, string Command, string State) ParseTaskBlock(string block)
    {
        string name = "(取得不可)";
        string author = "(取得不可)";
        string trigger = "(取得不可)";
        string command = "(取得不可)";
        string state = "(取得不可)";

        foreach (var raw in block.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None))
        {
            string l = raw.Trim();
            if (string.IsNullOrEmpty(l)) continue;

            if (l.StartsWith("タスク名:", StringComparison.OrdinalIgnoreCase))
                name = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("作成者:", StringComparison.OrdinalIgnoreCase))
                author = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("スケジュールの種類:", StringComparison.OrdinalIgnoreCase))
                trigger = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("トリガー:", StringComparison.OrdinalIgnoreCase))
                trigger = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("実行するタスク:", StringComparison.OrdinalIgnoreCase))
                command = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("実行コマンド:", StringComparison.OrdinalIgnoreCase))
                command = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("状態:", StringComparison.OrdinalIgnoreCase))
                state = l.Split(new[] { ':' }, 2)[1].Trim();
        }

        return (name, author, trigger, command, state);
    }

    // ============================================================================
    // システム・大手製アプリ除外フィルタ
    // ============================================================================
    static bool IsSystemTask(string name, string author, string cmd)
    {
        string n = (name ?? "").ToLower();
        string a = (author ?? "").ToLower();
        string c = (cmd ?? "").ToLower();

        string[] keywords =
        {
        "\\microsoft\\", "\\windows\\", "microsoft", "intel", "amd",
        "nvidia", "realtek", "system32", "languagecomponentsinstaller",
        "telemetry", "devicecensus", "edgeupdate", "defrag", "appx"
    };

        if (keywords.Any(k => n.Contains(k) || a.Contains(k) || c.Contains(k)))
            return true;

        if (author == "(取得不可)" && cmd == "(取得不可)")
            return true;

        return false;
    }

    // ============================================================================
    // CMD 実行ユーティリティ
    // ============================================================================
    static string RunCmd(string cmd)
    {
        using (var p = new System.Diagnostics.Process())
        {
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c " + cmd;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.StandardOutputEncoding = Encoding.GetEncoding("shift_jis");
            p.StartInfo.StandardErrorEncoding = Encoding.GetEncoding("shift_jis");
            p.Start();

            string output = p.StandardOutput.ReadToEnd();
            p.WaitForExit();
            return output;
        }
    }


    // 削除ソフトの痕跡（イベントログ＋レジストリ）
    // 目的：ユーザーがアンインストールしたはずなのに、レジストリに痕跡が残っているソフトを検出する
    // 方法：InstallDateが空のレジストリキーや、イベントログの削除メッセージを元に推定
    static string OutputUninstallTraces()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? 削除ソフト痕跡（レジストリより推定）:");
        sb.AppendLine("※ 以下は「インストール履歴に痕跡が残っているが、削除された可能性があるソフト」です。");
        HashSet<string> detectedApps = new HashSet<string>();

        string[] uninstallKeys = {
        @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
        @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
    };

        foreach (string rootKey in uninstallKeys)
        {
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(rootKey))
            {
                if (key == null) continue;

                foreach (string subkeyName in key.GetSubKeyNames())
                {
                    using (RegistryKey subkey = key.OpenSubKey(subkeyName))
                    {
                        string displayName = subkey?.GetValue("DisplayName")?.ToString();
                        string uninstallDate = subkey?.GetValue("InstallDate")?.ToString();

                        if (!string.IsNullOrEmpty(displayName) &&
                            string.IsNullOrEmpty(uninstallDate) &&
                            IsLikelyUserApp(displayName) &&
                            !detectedApps.Contains(displayName))
                        {
                            sb.AppendLine($"- {displayName}");
                            detectedApps.Add(displayName);
                        }
                    }
                }
            }
        }

        sb.AppendLine();
        sb.AppendLine("?? 削除ソフト痕跡（イベントログより確定）:");
        sb.AppendLine("※ 以下は Windows インストーラーの削除記録に基づく一覧です。日時は削除実行時点を示します。");
        try
        {
            EventLog appLog = new EventLog("Application");
            int count = 0;
            foreach (EventLogEntry entry in appLog.Entries)
            {
                if (entry.Source == "MsiInstaller" && entry.EntryType == EventLogEntryType.Information)
                {
                    if (entry.Message.Contains("削除") || entry.Message.Contains("アンインストール"))
                    {
                        string msg = entry.Message;
                        string productName = ExtractBetween(msg, "製品名: ", "、");
                        string time = entry.TimeGenerated.ToString("yyyy/MM/dd HH:mm");
                        if (!string.IsNullOrEmpty(productName) && IsLikelyUserApp(productName))
                        {
                            sb.AppendLine($"- {productName}（ {time}）");
                        }
                    }

                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"（イベントログ取得失敗: {ex.Message}）");
            //sb.AppendLine($"（イベントログ取得失敗: {ex.Message}）");
            return "";
        }
        return sb.ToString();
    }


    static bool IsLikelyUserApp(string name)
    {
        string[] ignoreKeywords = {
          "update", "security update", "hotfix", "patch", "language pack",
          "redistributable", "runtime", "sdk", "driver", "framework", "help viewer",
         "service pack", "help 更新", "visual c++", ".net", "targeting pack",
         "host fx resolver", "templates", "manifest", "apphost pack", "workload",
         "emscripten", "mono", "maui", "macos", "ios", "android", "tvos", "xamarin",
        "intellisense", "extension sdk", "desktop runtime", "toolset", "clickonce",
           "vs_", "windows sdk", "diagnostic", "crt", "lib", "headers", "tools"
};
        if (string.IsNullOrEmpty(name)) return false;
        string lowerName = name.ToLower();
        return !ignoreKeywords.Any(k => lowerName.Contains(k));
    }

    static string ExtractBetween(string source, string start, string end)
    {
        int startIndex = source.IndexOf(start);
        if (startIndex == -1) return null;
        startIndex += start.Length;
        int endIndex = source.IndexOf(end, startIndex);
        if (endIndex == -1) return null;
        return source.Substring(startIndex, endIndex - startIndex).Trim();
    }


    //ユーザー情報
    static string OutputUserAccounts()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ユーザーアカウント一覧：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT Name, Disabled, Status FROM Win32_UserAccount WHERE LocalAccount = TRUE");
            foreach (ManagementObject user in searcher.Get())
            {
                string name = user["Name"]?.ToString() ?? "(取得不可)";
                string status = user["Status"]?.ToString() ?? "(状態不明)";
                bool disabled = user["Disabled"] is bool d && d;

                sb.AppendLine($"■ ユーザー: {name}");
                sb.AppendLine($"  状態: {status}");
                sb.AppendLine($"  有効: {(disabled ? "無効" : "有効")}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ユーザー情報の取得に失敗しました");
            //sb.AppendLine("-> ユーザー情報の取得に失敗しました: " + ex.Message);
            return "";
        }
        return sb.ToString();
    }

    // レジストリ (HKEY_LOCAL_MACHINE) からインストール済みソフトウェア情報を取得
    static string OutputSoftwareInfo()
    {
        var sb = new StringBuilder();
        var apps = new List<InstalledApp>();
        // 64bit/32bit両方のレジストリパスから情報を検索
        string[] registryPaths = new[]
        {
            @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", // 64bitアプリケーション
            @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" // 32bitアプリケーション (WOW64)
        };

        foreach (string path in registryPaths)
        {
            // HKEY_LOCAL_MACHINE (ローカルPC全体の設定) を開く
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(path))
            {
                if (key == null) continue;

                foreach (string subkeyName in key.GetSubKeyNames())
                {
                    using (RegistryKey subkey = key.OpenSubKey(subkeyName))
                    {
                        string name = subkey?.GetValue("DisplayName") as string;
                        string dateStr = subkey?.GetValue("InstallDate") as string; // yyyyMMdd形式で格納されていることが多い

                        if (!string.IsNullOrEmpty(name))
                        {
                            DateTime? installDate = null;
                            // 日付文字列のパースを試みる
                            if (!string.IsNullOrEmpty(dateStr) && dateStr.Length == 8)
                            {
                                if (DateTime.TryParseExact(dateStr, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsedDate))
                                {
                                    installDate = parsedDate;
                                }
                            }
                            // "Update" や "Hotfix" を含むノイズをフィルタリング
                            if (!name.Contains("Update") && !name.Contains("Hotfix"))
                            {
                                apps.Add(new InstalledApp { Name = name, InstallDate = installDate });
                            }
                        }
                    }
                }
            }
        }

        // インストール日で降順に並べ替え、最新の20件を抽出
        var recentApps = apps
            .Where(a => a.InstallDate.HasValue) // 日付が取得できたもののみ
            .OrderByDescending(a => a.InstallDate.Value)
            .Take(MaxDataOutput)
            .ToList();

        sb.AppendLine("?? 最近インストールされたソフトウェア（最大" + MaxDataOutput + "件）:");
        foreach (var app in recentApps)
        {
            sb.AppendLine($"{app.InstallDate.Value:yyyy-MM-dd} - {app.Name}");
        }
        return sb.ToString();
    }


    static string OutputEnvironmentVariables()
    {

        var sb = new StringBuilder();
        sb.AppendLine("?? 環境変数（" + (expertMode ? "詳細表示（-Eモード）" : "ユーザー環境変数のみ") + "）：");

        // ユーザー環境変数
        var userVariables = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
        sb.AppendLine("■ ユーザー環境変数:");
        foreach (DictionaryEntry entry in userVariables)
        {
            string key = entry.Key?.ToString();
            string value = entry.Value?.ToString();
            if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
            {
                sb.AppendLine($"{key}: {value}");
            }
        }

        // -Eモード時のみシステム環境変数を表示
        if (expertMode)
        {
            var systemVariables = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
            sb.AppendLine("■ システム環境変数:");

            // Pathを分割して最大5件まで表示
            if (systemVariables["Path"] is string path)
            {
                sb.AppendLine("Path（最大5件）:");
                string[] paths = path.Split(';');
                foreach (string p in paths.Take(5))
                {
                    sb.AppendLine($"  - {p}");
                }
            }

            // ComSpecなどの重要変数
            if (systemVariables["ComSpec"] is string comspec)
            {
                sb.AppendLine($"ComSpec: {comspec}");
            }
        }
        return sb.ToString();
    }

    static string GetFilteredServices()
    {
        var sb = new StringBuilder();

        // サマリー用カウンタ
        int total = 0, running = 0, stopped = 0, disabled = 0;
        int count = 0;

        // 例外リスト（残したいMicrosoft系サービス）
        var exceptionList = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
    {
        "WSearch", "EventLog"
    };

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service");
            foreach (ManagementObject service in searcher.Get())
            {
                total++;

                string name = service["DisplayName"]?.ToString() ?? service["Name"]?.ToString();
                string processName = service["Name"]?.ToString() ?? "";
                string state = service["State"]?.ToString();
                string startMode = service["StartMode"]?.ToString();
                string status = service["Status"]?.ToString();

                // サマリー集計
                if (state == "Running")
                    running++;
                else if (startMode == "Disabled")
                    disabled++;
                else
                    stopped++;

                // 異常サービス抽出
                bool isSuspicious = (state == "Stopped" && startMode == "Auto") || status != "OK";
                bool isSystemService =
                    (name.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) ||
                     name.StartsWith("Windows", StringComparison.OrdinalIgnoreCase) ||
                     processName.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) ||
                     processName.StartsWith("Windows", StringComparison.OrdinalIgnoreCase)) &&
                    !exceptionList.Contains(processName);

                if (isSuspicious && !isSystemService)
                {
                    if (count == 0)
                        sb.AppendLine("?? サービス一覧（異常・停止・無効のみ (最大" + MaxDataOutput + "件）:");
                    sb.AppendLine($"- {name}: {state} ({startMode})");
                    count++;
                    if (count >= MaxDataOutput) break;
                }
            }

            // サマリー出力
            sb.Insert(0, $"稼働中: {running} 件\n停止中: {stopped} 件\n無効: {disabled} 件\n");
            sb.Insert(0, $"全サービス数: {total} 件\n");
            sb.Insert(0, "?? サービスサマリー：");

            if (count == 0)
            {
                sb.AppendLine("異常なサービスは検出されませんでした。");
            }
        }
        catch
        {
            Console.WriteLine("サービス一覧の取得に失敗しました。");
            //sb.AppendLine("サービス一覧の取得に失敗しました。");
            return "";
        }

        return sb.ToString();

    }

    // ----------------------------------------------
    // Windows標準以外のサービス一覧（最大 MaxDataOutput 件）
    // Windows 標準サービスは除外し、後から追加されたサービスのみ抽出
    // ----------------------------------------------
    static string OutputThirdPartyServices()
    {
        var sb = new StringBuilder();
        int count = 0;

        try
        {
            sb.AppendLine("?? Windows標準以外のサービス一覧（最大 " + MaxDataOutput + " 件）");

            // Win32_Service を列挙
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service");
            var services = searcher.Get();

            foreach (ManagementObject svc in services)
            {
                if (count >= MaxDataOutput)
                    break;

                string name = svc["Name"]?.ToString() ?? "";
                string displayName = svc["DisplayName"]?.ToString() ?? "";
                string state = svc["State"]?.ToString() ?? "";
                string startMode = svc["StartMode"]?.ToString() ?? "";
                string pathName = svc["PathName"]?.ToString() ?? "";

                // パスが空ならスキップ
                if (string.IsNullOrWhiteSpace(pathName))
                    continue;

                // ----------------------------------------------
                // Windows 標準サービスの判定
                // ----------------------------------------------
                string lowerPath = pathName.ToLower();

                bool isWindowsService =
                    lowerPath.Contains(@"\windows\system32") ||
                    lowerPath.Contains(@"\windows\syswow64") ||
                    lowerPath.Contains(@"\windows\servicing") ||
                    lowerPath.Contains("svchost.exe");

                if (isWindowsService)
                    continue; // 標準サービスは除外

                // ----------------------------------------------
                // サードパーティーサービスとして出力
                // ----------------------------------------------
                sb.AppendLine($"- {displayName} ({name})");
                sb.AppendLine($"  状態: {state} ({startMode})");
                sb.AppendLine($"  パス: {pathName}");
                sb.AppendLine();

                count++;
            }

            if (count == 0)
            {
                sb.AppendLine("  ※ サードパーティーサービスは見つかりませんでした");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("  ※ サードパーティーサービス取得中に例外発生: " + ex.Message);
            return "";
            //sb.AppendLine("  ※ サードパーティーサービス取得中に例外発生: " + ex.Message);
        }

        return sb.ToString();
    }

    //セクション抽出関数 簡易JSON用
    static string ExtractSection(string fullText, string header)
    {
        int start = fullText.IndexOf(header);
        if (start < 0) return "";

        int nextHeader = fullText.IndexOf("?? ", start + header.Length);
        if (nextHeader < 0) nextHeader = fullText.Length;

        return fullText.Substring(start, nextHeader - start).Trim();
    }

    //ファイアウォール概要
    static string GetFirewallProfileStatus()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ファイアウォールプロファイル状態:");

        try
        {
            var scope = new ManagementScope(@"\\.\root\StandardCimv2");
            var query = new ObjectQuery("SELECT * FROM MSFT_NetFirewallProfile");
            var searcher = new ManagementObjectSearcher(scope, query);

            foreach (ManagementObject profile in searcher.Get())
            {
                string name = profile["Name"]?.ToString();
                string enabled = profile["Enabled"]?.ToString() == "1" ? "有効" : "無効";
                string inbound = profile["DefaultInboundAction"]?.ToString() == "1" ? "許可" : "ブロック";
                string outbound = profile["DefaultOutboundAction"]?.ToString() == "1" ? "許可" : "ブロック";

                sb.AppendLine($"- {name}: {enabled}（受信: {inbound}, 送信: {outbound}）");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[GetFirewallProfileStatus] エラー: {ex.Message}");
            //sb.AppendLine($"[GetFirewallProfileStatus] エラー: {ex.Message}");
            return "";
        }

        return sb.ToString();
    }

    //ユーザー・アプリが設定したファイアウォールルール(-Eモード専用)
    static string OutputFirewallRules_E()
    {
        var sb = new StringBuilder();
        string output = RunCommand("netsh", "advfirewall firewall show rule name=all");
        List<string> customRules = ExtractCustomFirewallRules(output);

        //Console.WriteLine("== ユーザー/アプリが設定したファイアウォールルール ==");
        sb.AppendLine("?? ユーザー/アプリが設定したファイアウォールルール:");
        foreach (var rule in customRules)
        {
            //sb.AppendLine(rule);
            sb.AppendLine($"- {rule}:");
            //sb.AppendLine();
            //Console.WriteLine(rule);
            //Console.WriteLine(); // セパレータ
        }
        //Console.WriteLine(sb);
        return sb.ToString();
    }

    static string RunCommand(string fileName, string arguments)
    {
        var psi = new ProcessStartInfo
        {
            FileName = fileName,
            Arguments = arguments,
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true,
            StandardOutputEncoding = Encoding.UTF8
        };

        using (var process = Process.Start(psi))
        {
            string output = process.StandardOutput.ReadToEnd();
            process.WaitForExit();
            return output;
        }
    }

    static List<string> ExtractCustomFirewallRules(string netshOutput)
    {
        var results = new List<string>();
        var ruleNames = new HashSet<string>();

        try
        {
            var blocks = netshOutput.Split(new[] { "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var block in blocks)
            {
                string lower = block.ToLower();
                if (IsExcluded(lower)) continue;

                var lines = block.Split(new[] { "\r\n" }, StringSplitOptions.None);
                var ruleLine = Array.Find(lines, line => line.TrimStart().StartsWith("規則名:"));
                if (ruleLine != null)
                {
                    string ruleName = ruleLine.Substring(ruleLine.IndexOf("規則名:") + "規則名:".Length).Trim();
                    ruleNames.Add(ruleName);
                }
            }

            //Console.WriteLine("== ユーザー/アプリが設定したファイアウォールルール ==\n");
            foreach (var name in ruleNames)
            {
                //Console.WriteLine($"- {name}");
            }

            results.AddRange(ruleNames);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ExtractCustomFirewallRules] エラー: {ex.Message}");
        
        }

        return results;
    }



    static bool IsExcluded(string lower)
    {
        return
            lower.Contains("有効:") && lower.Contains("いいえ") ||
            lower.Contains("規則名:") && (
                lower.Contains("microsoft") ||
                lower.Contains("windows") ||
                lower.Contains("xbox") ||
                lower.Contains("game bar") ||
                lower.Contains("delivery optimization") ||
                lower.Contains("print 3d") ||
                lower.Contains("widgets") ||
                lower.Contains("web experience") ||
                lower.Contains("コア ネットワーク") ||
                lower.Contains("リモート アシスタンス") ||
                lower.Contains("ms-resource:") ||
                lower.Contains("@{")
            ) ||
            lower.Contains("グループ:") && (
                lower.Contains("microsoft") ||
                lower.Contains("windows") ||
                lower.Contains("xbox") ||
                lower.Contains("game bar") ||
                lower.Contains("delivery optimization") ||
                lower.Contains("print 3d") ||
                lower.Contains("widgets") ||
                lower.Contains("web experience") ||
                lower.Contains("コア ネットワーク") ||
                lower.Contains("リモート アシスタンス")
            );
    }

    //NTP同期状態
    static string OutputNtpStatus()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? NTP同期状態：");

        try
        {
            //sb.AppendLine(RunCmd("w32tm /query /status"));  //
            sb.AppendLine(RunCmd("w32tm /query /peers"));  //コマンド
        }
        catch
        {
            //return "NTP同期情報: 取得失敗";
            Console.WriteLine("NTP同期情報: 取得失敗");
            return "";
        }

        if (string.IsNullOrWhiteSpace(sb.ToString()))
        {
            Console.WriteLine("NTP同期情報: 取得失敗");
            return ""; 
            //return "NTP同期情報: 取得失敗";
        }

        return sb.ToString();
    }

    //システム詳細設定
    static string OutputSystemDetailSettings()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? システム詳細設定 ");

        try
        {
            //
            // 1. プロセッサのスケジュール設定
            //
            using (var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\PriorityControl"))
            {
                sb.AppendLine("■ プロセッサのスケジュール");

                if (key != null)
                {
                    object val = key.GetValue("Win32PrioritySeparation");
                    if (val != null)
                    {
                        int v = (int)val;

                        // bit2 が 1 → プログラム優先
                        bool isForeground = (v & 0x02) != 0;

                        sb.AppendLine("  設定: " + (isForeground ? "プログラムを優先" : "バックグラウンドサービスを優先"));
                        sb.AppendLine("  値: " + v);
                    }
                    else
                    {
                        sb.AppendLine("  Win32PrioritySeparation が見つかりません");
                    }
                }
                else
                {
                    sb.AppendLine("  PriorityControl キーが見つかりません");
                }
            }

            //
            // 2. 視覚効果（パフォーマンス優先か）
            //
            sb.AppendLine();
            sb.AppendLine("■ 視覚効果");

            using (var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\VisualEffects"))
            {
                if (key != null)
                {
                    object val = key.GetValue("VisualFXSetting");
                    if (val != null)
                    {
                        int v = (int)val;

                        string mode =
                            (v == 1) ? "パフォーマンス優先" :
                            (v == 2) ? "最高の外観" :
                            (v == 0) ? "自動調整" :
                            (v == 3) ? "カスタム" :
                            "不明 (" + v + ")";

                        sb.AppendLine("  設定: " + mode);
                        sb.AppendLine("  値: " + v);
                    }
                    else
                    {
                        sb.AppendLine("  VisualFXSetting が見つかりません");
                    }
                }
                else
                {
                    sb.AppendLine("  VisualEffects キーが見つかりません");
                }
            }

            //
            // 3. 仮想メモリ設定（ページファイル）
            //
            sb.AppendLine();
            sb.AppendLine("■ 仮想メモリ（ページファイル）");

            using (var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management"))
            {
                if (key != null)
                {
                    object val = key.GetValue("PagingFiles");
                    if (val != null)
                    {
                        string[] paging = (string[])val;
                        foreach (var p in paging)
                        {
                            sb.AppendLine("  " + p);
                        }
                    }
                    else
                    {
                        sb.AppendLine("  PagingFiles が見つかりません");
                    }
                }
                else
                {
                    sb.AppendLine("  Memory Management キーが見つかりません");
                }
            }

            //
            // 4. 電源プラン（powercfg）
            //
            sb.AppendLine();
            sb.AppendLine("■ 電源プラン");

            string powerPlan = RunCmd("powercfg /getactivescheme").Trim();
            if (string.IsNullOrEmpty(powerPlan))
            {
                sb.AppendLine("  取得失敗");
            }
            else
            {
                sb.AppendLine("  " + powerPlan);
            }

            //
            // 5. OneDrive 同期状態
            //
            sb.AppendLine();
            sb.AppendLine("■ OneDrive 同期状態");

            try
            {
                using (var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\OneDrive\Accounts\Personal"))
                {
                    if (key == null)
                    {
                        sb.AppendLine("  OneDrive アカウントが見つかりません");
                    }
                    else
                    {
                        string folder = key.GetValue("UserFolder") as string;
                        string status = key.GetValue("Status") as string;
                        string lastError = key.GetValue("LastError") as string;
                        string lastSync = key.GetValue("LastSyncTime")?.ToString();

                        sb.AppendLine($"  フォルダ: {folder}");

                        if (string.IsNullOrEmpty(status) &&
                            string.IsNullOrEmpty(lastError) &&
                            string.IsNullOrEmpty(lastSync))
                        {
                            sb.AppendLine("  同期状態情報はレジストリに存在しません（OneDrive の仕様）");
                        }
                        else
                        {
                            sb.AppendLine($"  状態: {status}");
                            sb.AppendLine($"  最終同期: {lastSync}");
                            sb.AppendLine($"  エラー: {lastError}");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("  OneDrive 状態取得エラー: " + ex.Message);
                return "";
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
            return "";
        }

        return sb.ToString();
    }

    //-E 専用 OneDrive 詳細解析
    static string OutputOneDriveDetail_E()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? OneDrive 詳細解析（-E 専用） ");

        try
        {
            //
            // 1. OneDrive.exe のプロセス状態
            //
            sb.AppendLine("■ OneDrive プロセス状態");

            var processes = Process.GetProcessesByName("OneDrive");
            if (processes.Length == 0)
            {
                sb.AppendLine("  OneDrive.exe は起動していません");
            }
            else
            {
                foreach (var p in processes)
                {
                    double cpu = 0;
                    try { cpu = p.TotalProcessorTime.TotalMilliseconds; } catch { }

                    sb.AppendLine($"  PID: {p.Id}, メモリ: {p.WorkingSet64 / 1024 / 1024} MB, CPU(ms): {cpu}");
                }
            }

            //
            // 2. OneDrive バージョン取得
            //
            sb.AppendLine();
            sb.AppendLine("■ OneDrive バージョン");

            try
            {
                string exePath = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    @"Microsoft\OneDrive\OneDrive.exe"
                );

                if (File.Exists(exePath))
                {
                    var ver = FileVersionInfo.GetVersionInfo(exePath);
                    sb.AppendLine($"  バージョン: {ver.FileVersion}");
                }
                else
                {
                    sb.AppendLine("  OneDrive.exe が見つかりません");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("  バージョン取得エラー: " + ex.Message);
                return "";
            }

            //
            // 3. OneDrive アカウント一覧（Personal / Business）
            //
            sb.AppendLine();
            sb.AppendLine("■ OneDrive アカウント一覧");

            string baseKey = @"Software\Microsoft\OneDrive\Accounts";
            using (var accounts = Registry.CurrentUser.OpenSubKey(baseKey))
            {
                if (accounts == null)
                {
                    sb.AppendLine("  アカウント情報が見つかりません");
                }
                else
                {
                    foreach (var sub in accounts.GetSubKeyNames())
                    {
                        using (var acc = accounts.OpenSubKey(sub))
                        {
                            string folder = acc.GetValue("UserFolder") as string;

                            if (string.IsNullOrEmpty(folder))
                                continue;

                            sb.AppendLine($"  [{sub}] フォルダ: {folder}");
                        }
                    }
                }
            }

            //
            // 4. ログ解析（Personal / Business）
            //
            sb.AppendLine();
            sb.AppendLine("■ OneDrive 同期ログ解析");

            string localApp = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            string[] logPaths =
            {
            Path.Combine(localApp, @"Microsoft\OneDrive\logs\Personal\SyncDiagnostics.log"),
            Path.Combine(localApp, @"Microsoft\OneDrive\logs\Business1\SyncDiagnostics.log"),
            Path.Combine(localApp, @"Microsoft\OneDrive\logs\Business2\SyncDiagnostics.log")
        };

            foreach (var logFile in logPaths)
            {
                if (!File.Exists(logFile))
                    continue;

                sb.AppendLine($"  ログ: {logFile}");

                var lines = File.ReadLines(logFile).Reverse().Take(200).ToList();

                string status = null;
                string lastError = null;
                string lastSuccess = null;

                foreach (var line in lines)
                {
                    if (status == null && (line.Contains("vaultState") || line.Contains("State")))
                        status = line.Trim();

                    if (lastError == null && (line.Contains("Error") || line.Contains("Failed")))
                        lastError = line.Trim();

                    if (lastSuccess == null && (line.Contains("Success") || line.Contains("Completed")))
                        lastSuccess = line.Trim();

                    if (status != null && lastError != null && lastSuccess != null)
                        break;
                }

                sb.AppendLine("    状態: " + (status ?? "不明"));
                sb.AppendLine("    最終成功: " + (lastSuccess ?? "成功ログなし"));
                sb.AppendLine("    最終エラー: " + (lastError ?? "なし"));
            }

            //
            // 5. 同期フォルダ容量チェック（Personal + Business）
            //
            sb.AppendLine();
            sb.AppendLine("■ 同期フォルダの容量・ファイル数");

            using (var accounts = Registry.CurrentUser.OpenSubKey(baseKey))
            {
                if (accounts != null)
                {
                    foreach (var sub in accounts.GetSubKeyNames())
                    {
                        using (var acc = accounts.OpenSubKey(sub))
                        {
                            string folder = acc.GetValue("UserFolder") as string;

                            if (string.IsNullOrEmpty(folder) || !Directory.Exists(folder))
                                continue;

                            long totalSize = 0;
                            long fileCount = 0;

                            foreach (var file in Directory.EnumerateFiles(folder, "*", SearchOption.AllDirectories))
                            {
                                try
                                {
                                    var info = new FileInfo(file);
                                    totalSize += info.Length;
                                    fileCount++;
                                }
                                catch { }
                            }

                            sb.AppendLine($"  [{sub}]");
                            sb.AppendLine($"    フォルダ: {folder}");
                            sb.AppendLine($"    ファイル数: {fileCount:N0}");
                            sb.AppendLine($"    合計サイズ: {totalSize / 1024 / 1024} MB");
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("OneDrive 詳細解析エラー: " + ex.Message);
            return "";
        }

        return sb.ToString();
    }




    /*
    //電源プラン
    static string OutputPowerPlan()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? 電源プラン：");

        try
        {
            sb.AppendLine(RunCmd("powercfg /getactivescheme"));
        }
        catch
        {
            Console.WriteLine("電源プラン: 取得失敗");
            return "";
            //return "電源プラン: 取得失敗";
        }

        if (string.IsNullOrWhiteSpace(sb.ToString()))
        {
            Console.WriteLine("電源プラン: 取得失敗");
            return "";   //return "電源プラン: 取得失敗";
        }

        return sb.ToString();
    }

    //プロセッサのスケジュール設定
    static string OutputProcessorScheduling()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? プロセッサのスケジュール設定");

        try
        {
            using (var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\PriorityControl"))
            {
                if (key != null)
                {
                    object val = key.GetValue("Win32PrioritySeparation");
                    if (val != null)
                    {
                        int v = (int)val;

                        string mode = (v == 0x18) ? "プログラムを優先" :
                                      (v == 0x26) ? "バックグラウンドサービスを優先" :
                                      "不明 (" + v + ")";

                        sb.AppendLine("設定: " + mode);
                        sb.AppendLine("値: " + v);
                    }
                    else
                    {
                        sb.AppendLine("Win32PrioritySeparation が見つかりません");
                    }
                }
                else
                {
                    sb.AppendLine("PriorityControl キーが見つかりません");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("取得エラー: " + ex.Message);
    return "";
        }

        return sb.ToString();
    }
    */

    //Edge 
    static string OutputEdgeInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? Edge 情報 ");
        sb.AppendLine("◆ Edge ブラウザ情報 ◆");

        try
        {
            // ============================
            // 1. Edge バージョン
            // ============================
            try
            {
                using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Edge\BLBeacon"))
                {
                    if (key != null)
                    {
                        object ver = key.GetValue("version");
                        if (ver != null)
                            sb.AppendLine("バージョン: " + ver.ToString());
                    }
                }
            }
            catch { }

            // ============================
            // 2. インストールパス
            // ============================
            try
            {
                using (var key = Registry.LocalMachine.OpenSubKey(
                    @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe"))
                {
                    if (key != null)
                    {
                        object path = key.GetValue("");
                        if (path != null)
                            sb.AppendLine("インストールパス: " + path.ToString());
                    }
                }
            }
            catch { }

            // ============================
            // 3. 既定ブラウザ判定
            // ============================
            try
            {
                using (var key = Registry.CurrentUser.OpenSubKey(
                    @"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice"))
                {
                    if (key != null)
                    {
                        object progId = key.GetValue("ProgId");
                        if (progId != null)
                        {
                            string p = progId.ToString();
                            if (p.IndexOf("MSEdgeHTM", StringComparison.OrdinalIgnoreCase) >= 0)
                                sb.AppendLine("既定ブラウザ: Edge");
                            else
                                sb.AppendLine("既定ブラウザ: " + p);
                        }
                    }
                }
            }
            catch { }

            // ============================
            // 4. プロファイル数
            // ============================
            try
            {
                string userData = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    @"Microsoft\Edge\User Data");

                if (Directory.Exists(userData))
                {
                    int count = 0;
                    string[] dirs = Directory.GetDirectories(userData);

                    foreach (string d in dirs)
                    {
                        string name = Path.GetFileName(d);
                        if (name.Equals("Default", StringComparison.OrdinalIgnoreCase) ||
                            name.StartsWith("Profile", StringComparison.OrdinalIgnoreCase))
                        {
                            count++;
                        }
                    }

                    sb.AppendLine("プロファイル数: " + count);
                }
            }
            catch { }

            // ============================
            // 5. 拡張機能数（Default プロファイル）
            // ============================
            try
            {
                string extPath = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    @"Microsoft\Edge\User Data\Default\Extensions");

                if (Directory.Exists(extPath))
                {
                    int extCount = Directory.GetDirectories(extPath).Length;
                    sb.AppendLine("拡張機能数: " + extCount);
                }
            }
            catch { }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Edge 情報取得エラー: " + ex.Message);
            return "";
        }
        if (expertMode)
        {
            sb.AppendLine();
            sb.Append(OutputEdgeCacheInfo());
        }

        return sb.ToString();
    }

    static string OutputEdgeCacheInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("◆ Edge キャッシュ・設定情報 ◆");

        try
        {
            string basePath = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                @"Microsoft\Edge\User Data\Default");

            // ============================
            // 1. キャッシュサイズ
            // ============================
            try
            {
                string cachePath = Path.Combine(basePath, "Cache");

                if (Directory.Exists(cachePath))
                {
                    long total = 0;
                    string[] files = Directory.GetFiles(cachePath, "*", SearchOption.AllDirectories);

                    foreach (string f in files)
                    {
                        try
                        {
                            FileInfo fi = new FileInfo(f);
                            total += fi.Length;
                        }
                        catch { }
                    }

                    long mb = total / (1024 * 1024);
                    sb.AppendLine("キャッシュサイズ: " + mb + " MB");
                    sb.AppendLine("キャッシュフォルダ: " + cachePath);
                }
            }
            catch { }

            // ============================
            // 2. Cookie / History / Bookmarks の存在確認
            // ============================
            try
            {
                string cookies = Path.Combine(basePath, "Cookies");
                if (File.Exists(cookies))
                    sb.AppendLine("Cookie DB: あり");

                string history = Path.Combine(basePath, "History");
                if (File.Exists(history))
                    sb.AppendLine("履歴 DB: あり");

                string bookmarks = Path.Combine(basePath, "Bookmarks");
                if (File.Exists(bookmarks))
                    sb.AppendLine("ブックマーク: あり");

                string prefs = Path.Combine(basePath, "Preferences");
                if (File.Exists(prefs))
                    sb.AppendLine("Preferences: あり");

                string securePrefs = Path.Combine(basePath, "Secure Preferences");
                if (File.Exists(securePrefs))
                    sb.AppendLine("Secure Preferences: あり");

                string localState = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    @"Microsoft\Edge\User Data\Local State");

                if (File.Exists(localState))
                    sb.AppendLine("Local State: あり");
            }
            catch { }

            // ============================
            // 3. 拡張機能数
            // ============================
            try
            {
                string extPath = Path.Combine(basePath, "Extensions");

                if (Directory.Exists(extPath))
                {
                    int extCount = Directory.GetDirectories(extPath).Length;
                    sb.AppendLine("拡張機能フォルダ数: " + extCount);
                }
            }
            catch { }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Edge キャッシュ情報取得エラー: " + ex.Message);
            return "";
        }

        return sb.ToString();
    }



    //デバイス状態取得
    static List<DriverInfo> GetPnPDeviceStatus()
    {
        var list = new List<DriverInfo>();

        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity"))
            {
                foreach (ManagementObject obj in searcher.Get())
                {
                    string name = obj["Name"]?.ToString();
                    if (string.IsNullOrWhiteSpace(name))
                        continue;

                    list.Add(new DriverInfo
                    {
                        DeviceName = name,
                        ErrorCode = Convert.ToInt32(obj["ConfigManagerErrorCode"] ?? 0)
                    });
                }
            }
        }
        catch
        {
            // 必要ならログ
        }

        return list;
    }



    //署名・日付取得
static List<DriverInfo> GetSignedDriverInfo()
{
    var list = new List<DriverInfo>();

    try
    {
        using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPSignedDriver"))
        {
            foreach (ManagementObject mo in searcher.Get())
            {
                string name = mo["DeviceName"]?.ToString();
                if (string.IsNullOrWhiteSpace(name))
                    continue;  // ★ 不明ドライバー除外（重要）

                string provider = mo["DriverProviderName"]?.ToString() ?? "";
                string inf = mo["InfName"]?.ToString() ?? "";
                bool isSigned = Convert.ToBoolean(mo["IsSigned"] ?? true);

                DateTime? driverDate = null;
                try
                {
                    string dateStr = mo["DriverDate"]?.ToString();
                    if (!string.IsNullOrEmpty(dateStr))
                        driverDate = ManagementDateTimeConverter.ToDateTime(dateStr);
                }
                catch
                {
                    driverDate = null;
                }

                // Intel 1968 年問題除外
                if (driverDate.HasValue && driverDate.Value.Year < 1980)
                    continue;

                list.Add(new DriverInfo
                {
                    DeviceName = name,
                    Version = mo["DriverVersion"]?.ToString(),
                    Provider = provider,
                    InfName = inf,
                    DriverDate = driverDate,
                    IsSigned = isSigned
                });
            }
        }
    }
    catch
    {
        // 必要ならログ
    }

    return list;
}



    //統合ロジック
    static (List<DriverInfo> oldDrivers, List<DriverInfo> badDrivers)
        AnalyzeDrivers()
    {
        var pnp = GetPnPDeviceStatus();
        var signed = GetSignedDriverInfo();

        // 署名情報と PnP 情報をマージ
        foreach (var s in signed)
        {
            var p = pnp.FirstOrDefault(x => x.DeviceName == s.DeviceName);
            if (p != null)
                s.ErrorCode = p.ErrorCode;
        }

        // 古いドライバー（サードパーティ製）
        var oldDrivers = signed
            .Where(d => d.IsThirdParty)
            .OrderBy(d => d.DriverDate ?? DateTime.MaxValue)
            .Take(MaxDataOutput)
            .ToList();

        // 異常ドライバー
        var badDrivers = signed
            .Where(d => d.IsBad)
            .ToList();

        return (oldDrivers, badDrivers);
    }

    //出力メソッド
    static string OutputDriverAnalysis()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ドライバー情報（最大" + MaxDataOutput + "件）");

        var (oldDrivers, badDrivers) = AnalyzeDrivers();
        if (expertMode)
        {
            // --- ドライバー一覧 ---
            //sb.AppendLine();
            sb.AppendLine("[ドライバー一覧 （サードパーティ製・古い順）]");

            if (oldDrivers.Count == 0)
            {
                sb.AppendLine("- なし");
            }
            else
            {
                foreach (var d in oldDrivers)
                {
                    string date = d.DriverDate?.ToString("yyyy-MM-dd") ?? "不明";
                    sb.AppendLine($"- {date}  {d.DeviceName}");
                    if (expertMode)
                    {
                        sb.AppendLine($"    Version : {d.Version}");
                        sb.AppendLine($"    Provider: {d.Provider}");
                        sb.AppendLine($"    INF     : {d.InfName}");
                    }
                }
            }
        }
        // --- 不明なデバイスや未署名ドライバー ---
        sb.AppendLine();
        sb.AppendLine("[不明なデバイスや未署名ドライバー]");
        if (!expertMode)
            sb.AppendLine("※ 不明なデバイスや未署名ドライバーを検出しました。一覧･詳細は -E モードで確認できます。");
        if (badDrivers.Count == 0)
        {
            sb.AppendLine("- なし");
        }
        else
        {
            foreach (var d in badDrivers)
            {
                sb.AppendLine($"- {d.DeviceName}");
                if (expertMode)
                {
                    sb.AppendLine($"    署名: {(d.IsSigned ? "署名あり" : "未署名")}");
                    sb.AppendLine($"    エラーコード: {d.ErrorCode}");
                }
            }
        }

        return sb.ToString();
    }

    //ドメイン / ワークグループ情報
    [DllImport("Netapi32.dll", CharSet = CharSet.Unicode)]
    static extern int NetGetJoinInformation(
        string server,
        out IntPtr domain,
        out int status);

    [DllImport("Netapi32.dll")]
    static extern int NetApiBufferFree(IntPtr buffer);

    static string OutputDomainInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ドメイン / ワークグループ情報 ");

        try
        {
            IntPtr pDomain;
            int status;

            // server = null → ローカルマシン
            int result = NetGetJoinInformation(null, out pDomain, out status);

            if (result == 0) // NERR_Success
            {
                string name = Marshal.PtrToStringUni(pDomain);
                NetApiBufferFree(pDomain);

                switch (status)
                {
                    case 1: // NetSetupDomainName
                        sb.AppendLine("参加状態: ドメイン");
                        sb.AppendLine("ドメイン名: " + name);
                        break;

                    case 2: // NetSetupWorkgroupName
                        sb.AppendLine("参加状態: ワークグループ");
                        sb.AppendLine("ワークグループ名: " + name);
                        break;

                    default:
                        sb.AppendLine("参加状態: 不明");
                        break;
                }
            }
            else
            {
                Console.WriteLine("ドメイン情報取得に失敗しました (コード: " + result + ")");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ドメイン情報取得エラー: " + ex.Message);
            return "";
        }

        return sb.ToString();
    }

    static string OutputPrintersBasic()
    {
        var sb = new StringBuilder();

        if (!expertMode)
        {
            sb.AppendLine("?? プリンター一覧（基本） ");

            // --- Print Spooler サービス状態 ---
            try
            {
                using (var sc = new System.ServiceProcess.ServiceController("Spooler"))
                {
                    sb.AppendLine("■ Print Spooler サービス状態");
                    sb.AppendLine($"  状態: {sc.Status}");
                    sb.AppendLine($"  StartType: {GetServiceStartType("Spooler")}");
                    sb.AppendLine();
                }
            }
            catch (Exception ex)
            {
                // ★ sb に書かない → 再取得対象
                Console.WriteLine("Spooler 状態取得エラー: " + ex.Message);
                return "";
            }

            // --- プリンター一覧 ---
            try
            {
                using (var searcher = new ManagementObjectSearcher("SELECT Name FROM Win32_Printer"))
                {
                    var results = searcher.Get();

                    if (results.Count == 0)
                    {
                        sb.AppendLine("  （プリンターなし）");
                    }
                    else
                    {
                        foreach (ManagementObject printer in results)
                        {
                            sb.AppendLine("  - " + printer["Name"]);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // ★ sb に書かない → 再取得対象
                Console.WriteLine("プリンター一覧取得エラー: " + ex.Message);
                return "";
            }

            return sb.ToString();
        }
        else
        {
            // -E の場合は詳細版へ
            sb.AppendLine(OutputPrintersDetail_E());
            return sb.ToString();
        }
    }

    static string OutputPrintersDetail_E()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? プリンター詳細情報（-E 専用） ");

        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer"))
            {
                foreach (ManagementObject printer in searcher.Get())
                {
                    sb.AppendLine($"■ {printer["Name"]}");
                    sb.AppendLine($"  状態: {printer["PrinterStatus"]}");
                    sb.AppendLine($"  デフォルト: {printer["Default"]}");
                    sb.AppendLine($"  種類: {(printer["Network"] is bool net && net ? "ネットワーク" : "ローカル")}");
                    sb.AppendLine($"  ドライバー: {printer["DriverName"]}");
                    sb.AppendLine($"  ポート: {printer["PortName"]}");
                    sb.AppendLine($"  ジョブ数: {printer["JobCountSinceLastReset"]}");
                    sb.AppendLine();
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("プリンター詳細取得エラー: " + ex.Message);
            return "";
        }

        return sb.ToString();
    }


    //クラッシュ・異常終了サマリ
    // ===============================
    //  クラッシュ・異常終了サマリ（最終完成版）
    // ===============================
    static string OutputCrashSummary_E()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? クラッシュ・異常終了サマリ（-E 専用）");

        try
        {
            var werGroups = new Dictionary<string, WerGroup>();
            var osGroups = new OsErrorGroup();
            var unified = new List<UnifiedEvent>();

            // --- Application ログ（1000/1001） ---
            try
            {
                EventLog appLog = new EventLog("Application");

                foreach (var entry in appLog.Entries.Cast<EventLogEntry>())
                {
                    if (entry.EventID == 1001 && entry.Source == "Windows Error Reporting")
                    {
                        var parsed = ParseWer(entry.Message);
                        if (parsed == null) continue;

                        // 種類0（空バケット）は除外
                        if (parsed.BucketType == "0" || parsed.Bucket == "") continue;

                        string key = $"{parsed.P1}|{parsed.EventName}|{parsed.P4}|{parsed.Bucket}";

                        if (!werGroups.ContainsKey(key))
                        {
                            werGroups[key] = new WerGroup
                            {
                                P1 = parsed.P1,
                                EventName = parsed.EventName,
                                P4 = parsed.P4,
                                Bucket = parsed.Bucket,
                                Latest = entry.TimeGenerated,
                                Count = 1
                            };
                        }
                        else
                        {
                            werGroups[key].Count++;
                            if (entry.TimeGenerated > werGroups[key].Latest)
                                werGroups[key].Latest = entry.TimeGenerated;
                        }
                    }
                    else if (entry.EventID == 1000)
                    {
                        unified.Add(new UnifiedEvent
                        {
                            Time = entry.TimeGenerated,
                            Category = "アプリクラッシュ",
                            Text = $"EventID 1000: {entry.Message}"
                        });
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Application ログ取得エラー: " + ex.Message);
                return "";
            }

            // --- System ログ（41/6008 のみセット化） ---
            try
            {
                EventLog sysLog = new EventLog("System");

                foreach (var entry in sysLog.Entries.Cast<EventLogEntry>())
                {
                    if (entry.EventID == 41)
                    {
                        osGroups.Add41(entry.TimeGenerated);
                    }
                    else if (entry.EventID == 6008)
                    {
                        osGroups.Add6008(entry.TimeGenerated);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("System ログ取得エラー: " + ex.Message);
                return "";
            }

            // --- OS 異常終了を unified に追加 ---
            if (osGroups.Count > 0)
            {
                unified.Add(new UnifiedEvent
                {
                    Time = osGroups.Latest,
                    Category = "OS異常終了",
                    Text =
                        $"OS 異常終了（Kernel-Power 41 / Unexpected Shutdown 6008）\n" +
                        $"  発生回数: {osGroups.Count} 回"
                });
            }

            // --- WER グループを unified に追加（分類付き） ---
            foreach (var g in werGroups.Values)
            {
                unified.Add(new UnifiedEvent
                {
                    Time = g.Latest,
                    Category = ClassifyWer(g),
                    Text =
                        $"{g.P1} / {g.EventName} / {g.P4}\n" +
                        $"  発生回数: {g.Count} 回\n" +
                        $"  障害バケット: {g.Bucket}"
                });
            }

            // --- 全イベントを時系列で統合して MaxDataOutput 件に絞る ---
            var finalList = unified
                .OrderByDescending(e => e.Time)
                .Take(MaxDataOutput)
                .ToList();

            if (finalList.Count == 0)
            {
                sb.AppendLine("  （クラッシュ・異常終了の記録はありません）");
                return sb.ToString();
            }

            // --- カテゴリごとに出力 ---
            var categories = finalList
                .GroupBy(e => e.Category)
                .OrderBy(g => g.Key);

            foreach (var group in categories)
            {
                sb.AppendLine($"=== {group.Key} ===");

                foreach (var ev in group.OrderByDescending(e => e.Time))
                {
                    sb.AppendLine($"[{ev.Time}]");
                    sb.AppendLine(ev.Text);
                    sb.AppendLine();
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("クラッシュサマリ取得エラー: " + ex.Message);
            return "";
        }

        return sb.ToString();
    }


    // ===============================
    //  クラス定義
    // ===============================

    class WerParsed
    {
        public string Bucket;
        public string BucketType;
        public string EventName;
        public string P1;
        public string P4;
    }

    class WerGroup
    {
        public string P1;
        public string EventName;
        public string P4;
        public string Bucket;
        public DateTime Latest;
        public int Count;
    }

    class UnifiedEvent
    {
        public DateTime Time;
        public string Category;
        public string Text;
    }

    class OsErrorGroup
    {
        public int Count = 0;
        public DateTime Latest = DateTime.MinValue;

        public void Add41(DateTime t)
        {
            Count++;
            if (t > Latest) Latest = t;
        }

        public void Add6008(DateTime t)
        {
            Count++;
            if (t > Latest) Latest = t;
        }
    }


    // ===============================
    //  WER メッセージ解析
    // ===============================
    static WerParsed ParseWer(string message)
    {
        var parsed = new WerParsed();
        var lines = message.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

        foreach (var line in lines)
        {
            if (line.StartsWith("障害バケット"))
            {
                var parts = line.Replace("障害バケット", "")
                                .Replace("種類", "")
                                .Replace("、", " ")
                                .Trim()
                                .Split(' ');

                if (parts.Length >= 2)
                {
                    parsed.Bucket = parts[0];
                    parsed.BucketType = parts[1];
                }
            }
            else if (line.StartsWith("イベント名"))
            {
                parsed.EventName = line.Replace("イベント名:", "").Trim();
            }
            else if (line.StartsWith("P1:"))
            {
                parsed.P1 = line.Replace("P1:", "").Trim();
            }
            else if (line.StartsWith("P4:"))
            {
                parsed.P4 = line.Replace("P4:", "").Trim();
            }
        }

        if (parsed.P1 == null || parsed.EventName == null)
            return null;

        return parsed;
    }


    // ===============================
    //  WER の分類
    // ===============================
    static string ClassifyWer(WerGroup g)
    {
        string e = g.EventName.ToLower();
        string p1 = g.P1.ToLower();

        if (e.Contains("appcrash") || e.Contains("crashpad") || e.Contains("livekernelevent"))
            return "アプリクラッシュ";

        if (e.Contains("hang") || e.Contains("apphang"))
            return "アプリハング";

        if (e.Contains("storeagent") || e.Contains("update"))
            return "Windows Update 失敗";

        if (e.Contains("backup"))
            return "バックアップ失敗";

        return "その他の異常";
    }






    /*
    static string OutputCrashSummary_E()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? クラッシュ・異常終了サマリ（-E 専用）");

        try
        {
            var events = new List<EventLogEntry>();

            // --- Application ログ（1000/1001） ---
            try
            {
                EventLog appLog = new EventLog("Application");

                foreach (var entry in appLog.Entries.Cast<EventLogEntry>())
                {
                    if ((entry.EventID == 1000 || entry.EventID == 1001) &&
                        (entry.Source == "Application Error" || entry.Source == "Windows Error Reporting"))
                    {
                        events.Add(entry);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Application ログ取得エラー: " + ex.Message);
    return "";
            }

            // --- System ログ（41/6008/1074/6005/6006） ---
            try
            {
                EventLog sysLog = new EventLog("System");

                foreach (var entry in sysLog.Entries.Cast<EventLogEntry>())
                {
                    if (entry.EventID == 41 || entry.EventID == 6008 ||
                        entry.EventID == 1074 || entry.EventID == 6005 || entry.EventID == 6006)
                    {
                        events.Add(entry);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("System ログ取得エラー: " + ex.Message);
    return "";
            }

            // --- 時系列で新しい順に並べる ---
            var sorted = events.OrderByDescending(e => e.TimeGenerated)
                               .Take(MaxDataOutput)
                               .ToList();

            if (sorted.Count == 0)
            {
                sb.AppendLine("  （クラッシュ・異常終了の記録はありません）");
                return sb.ToString();
            }

            // --- 出力 ---
            foreach (var entry in sorted)
            {
                sb.AppendLine($"[{entry.TimeGenerated}] EventID: {entry.EventID}");
                sb.AppendLine($"  Source: {entry.Source}");

                if (entry.EventID == 1001)
                {
                    sb.Append(ExtractWerSummary(entry.Message));
                }
                else
                {
                    sb.AppendLine($"  Message: {entry.Message}");
                }

                sb.AppendLine();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("クラッシュサマリ取得エラー: " + ex.Message);
            return "";
        }

        return sb.ToString();
    }

    static string ExtractWerSummary(string message)
    {
        var sb = new StringBuilder();
        var lines = message.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

        foreach (var line in lines)
        {
            if (line.StartsWith("障害バケット") ||
                line.StartsWith("イベント名") ||
                line.StartsWith("P1:") ||
                line.StartsWith("P2:") ||
                line.StartsWith("P3:") ||
                line.StartsWith("P4:") ||
                line.StartsWith("レポート ID"))
            {
                sb.AppendLine("  " + line);
            }
        }

        return sb.ToString();
    }
    */



    //WSearch(-E専用)
    static string OutputWSearchDetail_E()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? Windows Search（WSearch）詳細情報（-E 専用） ");

        try
        {
            // --- サービス状態 ---
            try
            {
                using (var sc = new System.ServiceProcess.ServiceController("WSearch"))
                {
                    sb.AppendLine($"サービス状態: {sc.Status}");
                    sb.AppendLine($"StartType: {GetServiceStartType("WSearch")}");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("WSearch サービス状態取得エラー: " + ex.Message);
                
            }

            // --- インデックス状態（WMI） ---
            try
            {
                using (var searcher = new ManagementObjectSearcher(
                    "SELECT IndexStatus, NumberOfItems, NumberOfFailedItems FROM MSFT_SearchGatherer"))
                {
                    var results = searcher.Get();

                    if (results.Count == 0)
                    {
                        sb.AppendLine("インデックス情報: （MSFT_SearchGatherer クラスが存在しません）");
                    }
                    else
                    {
                        foreach (ManagementObject obj in results)
                        {
                            sb.AppendLine($"IndexStatus: {obj["IndexStatus"]}");
                            sb.AppendLine($"NumberOfItems: {obj["NumberOfItems"]}");
                            sb.AppendLine($"NumberOfFailedItems: {obj["NumberOfFailedItems"]}");
                        }
                    }
                }
            }
            catch (ManagementException mex) when (mex.Message.Contains("無効なクラス"))
            {
                // ★ Windows 11 24H2 では正常
                Console.WriteLine("インデックス情報: （MSFT_SearchGatherer クラスが OS に存在しません）");
                
            }
            catch (Exception ex)
            {
                Console.WriteLine("WSearch インデックス情報取得エラー: " + ex.Message);
                
            }

            // --- Windows.edb のサイズ ---
            try
            {
                string edbPath = @"C:\ProgramData\Microsoft\Search\Data\Applications\Windows\Windows.edb";
                if (File.Exists(edbPath))
                {
                    long size = new FileInfo(edbPath).Length;
                    sb.AppendLine($"Windows.edb サイズ: {size / (1024 * 1024)} MB");
                }
                else
                {
                    sb.AppendLine("Windows.edb: （存在しません）");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Windows.edb サイズ取得エラー: " + ex.Message);
                
            }

            // --- インデックス対象フォルダ（レジストリ） ---
            try
            {
                sb.AppendLine("インデックス対象フォルダ:");

                using (var key = Registry.LocalMachine.OpenSubKey(
                    @"SOFTWARE\Microsoft\Windows Search\Gather\Windows\SystemIndex\Sites"))
                {
                    if (key != null)
                    {
                        foreach (var sub in key.GetSubKeyNames())
                        {
                            using (var site = key.OpenSubKey(sub))
                            {
                                string url = site?.GetValue("Url")?.ToString();
                                if (!string.IsNullOrEmpty(url))
                                    sb.AppendLine("  - " + url);
                            }
                        }
                    }
                    else
                    {
                        sb.AppendLine("  （取得できませんでした）");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("インデックス対象フォルダ取得エラー: " + ex.Message);
                
            }

            // --- 最近の WSearch 関連イベントログ ---
            try
            {
                sb.AppendLine("最近の WSearch 関連イベントログ（7031/7023）:");

                EventLog eventLog = new EventLog("System");
                int count = 0;

                foreach (EventLogEntry entry in eventLog.Entries.Cast<EventLogEntry>().Reverse())
                {
                    if (entry.Source == "Service Control Manager" &&
                        (entry.EventID == 7031 || entry.EventID == 7023) &&
                        entry.Message.Contains("WSearch"))
                    {
                        sb.AppendLine($"[{entry.TimeGenerated}] EventID: {entry.EventID}");
                        sb.AppendLine($"  Message: {entry.Message}");
                        sb.AppendLine();
                        count++;

                        if (count >= 5) break;
                    }
                }

                if (count == 0)
                    sb.AppendLine("  （該当イベントなし）");
            }
            catch (Exception ex)
            {
                Console.WriteLine("WSearch イベントログ取得エラー: " + ex.Message);
          
            }
        }
        catch (Exception ex)
        {
            // ★重要：sb に書かない → 再取得対象
            Console.WriteLine("WSearch 詳細取得エラー: " + ex.Message);
            return "";
        }

        return sb.ToString();
    }

    //ヘルパー
    static string GetServiceStartType(string serviceName)
    {
        try
        {
            using (var searcher = new ManagementObjectSearcher(
                $"SELECT StartMode FROM Win32_Service WHERE Name='{serviceName}'"))
            {
                foreach (ManagementObject obj in searcher.Get())
                {
                    return obj["StartMode"]?.ToString() ?? "不明";
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("StartType取得エラー: " + ex.Message);
        }
        return "不明";
    }


    //以下処理エンジン

    //処理速度と安定性の両立を目指す多段並列処理
    //段生成アルゴリズム
    //WMI使用数 <= 3
    //負荷合計 <= 13
    //ConflictGroupが重ならないように
    //を基準に自動で段を作る。
    
    static List<List<int>> BuildParallelPhases(bool expertMode)
    {
        // 対象メソッド一覧
        var indices = Enumerable.Range(0, MaxMethod)
            .Where(i => expertMode || !mappings[i].IsExtended)
            .ToList();

        // Best-Fit のために「重い順」にソート
        indices = indices
            .OrderByDescending(i => mappings[i].LoadLevel + mappings[i].WmiLevel)
            .ToList();

        var phases = new List<List<int>>();

        foreach (var idx in indices)
        {
            var m = mappings[idx];
            if (!m.Ran_Sw) continue;
            List<int> bestPhase = null;
            int bestScore = int.MaxValue;

            foreach (var phase in phases)
            {
                
                // 現在のフェーズの負荷
                byte wmiSum = (byte)phase.Sum(i => mappings[i].WmiLevel);
                byte loadSum = (byte)phase.Sum(i => mappings[i].LoadLevel);

                // ConflictGroup の扱いを「重い処理同士のみ禁止」に変更
                bool conflict = phase.Any(i =>
                {
                    var pm = mappings[i];

                    //bool bothHeavy = (pm.WmiLevel >= 2 || pm.LoadLevel >= 3) && (m.WmiLevel >= 2 || m.LoadLevel >= 3);
                    //bool bothHeavy = phase.Any(i => mappings[i].ConflictGroup == m.ConflictGroup);
                    //bool bothHeavy = (pm.WmiLevel + pm.LoadLevel) >= 3;
                    bool bothHeavy = m.ConflictGroup != 0 && phase.Any(i => mappings[i].ConflictGroup == m.ConflictGroup);






                    return bothHeavy && pm.ConflictGroup == m.ConflictGroup;
                });

                if (conflict) continue;

                // WMI / Load の制限
                if (wmiSum + m.WmiLevel > 3) continue;
                if (loadSum + m.LoadLevel > 13) continue;

                // スコア（小さいほど良い）
                int score = (wmiSum + m.WmiLevel) * 10 + (loadSum + m.LoadLevel);

                if (score < bestScore)
                {
                    bestScore = score;
                    bestPhase = phase;
                }
            }

            // 最適なフェーズに追加
            if (bestPhase != null)
            {
                bestPhase.Add(idx);
            }
            else
            {
                // 新しいフェーズを作成
                phases.Add(new List<int> { idx });
            }
        }

        return phases;
    }
    


    /*
    static List<List<int>> BuildParallelPhases(bool expertMode)
    {
        // 処理が軽い順にソート（Load → WMI）
        var indices = Enumerable.Range(0, MaxMethod)
            .Where(i => expertMode || !mappings[i].IsExtended)
            .OrderBy(i => mappings[i].LoadLevel)
            .ThenBy(i => mappings[i].WmiLevel)
            .ToList();

        var phases = new List<List<int>>();
        var current = new List<int>();

        byte wmi = 0;
        byte load = 0;

        foreach (var i in indices)
        {
            var m = mappings[i];

            bool canAdd =
                (wmi + m.WmiLevel <= 3) &&
                (load + m.LoadLevel <= 25);

            if (!canAdd)
            {
                phases.Add(current);
                current = new List<int>();
                wmi = 0;
                load = 0;
            }

            current.Add(i);
            wmi += m.WmiLevel;
            load += m.LoadLevel;
        }

        if (current.Count > 0)
            phases.Add(current);

        return phases;
    }
    */


    // PC内部の情報源から多段並列処理でデーター収集(-E用)
    //自動並列実行
    static async Task<string> GetAllSystemDataAsync_E(bool expertMode)
    {
        var sb = new StringBuilder();
        var phases = BuildParallelPhases(expertMode);
        int phaseNum = 1;

        foreach (var phase in phases)
        {
            //sb.AppendLine($"==== Phase {phaseNum} ====");
            Console.WriteLine($"==== Phase {phaseNum} ====");
            var tasks = phase.Select(index =>
               Task.Run(() => sb_data[index] = mappings[index].Method())
            ).ToArray();

            await Task.WhenAll(tasks);

            phaseNum++;
        }

        return sb.ToString();
    }

    // PC内部の情報源から全ての取得メソッドを無制限並列処理でデーター収集。最速モード(-F用)
    static async Task<string> GetAllSystemDataFastAsync(bool expertMode)
    {
        Console.WriteLine("==== FastMode: 全メソッド無制限並列実行 ====");
        var sb = new StringBuilder();

        var tasks = new List<Task>();

        for (int i = 0; i < MaxMethod; i++)
        {
            var m = mappings[i];
            if (!m.Ran_Sw) continue;
            if (!expertMode && m.IsExtended)
                continue;

            int index = i;

            tasks.Add(Task.Run(() =>
            {
                sb_data[index] = m.Method();
            }));
        }

        await Task.WhenAll(tasks);

        //sb.AppendLine("==== FastMode: 全メソッド無制限並列実行 ====");
        

        return sb.ToString();
    }
 
    
    //逐次処理でデーター収集。速度より安定性重視。(-Sモード、-Eおよびオプション無しリトライ) 
    static void GetSequential()
    {
        Console.WriteLine("==== SafetyMode: 逐次取得実行 ====");
        for (int i = 0; i < MaxMethod; i++)
        {
            var map = mappings[i];
            if (!map.Ran_Sw)        continue;


            if (!expertMode && map.IsExtended)
                continue;

            if (string.IsNullOrWhiteSpace(sb_data[i]))
            {
                sb_data[i] = map.Method();
                Console.WriteLine(map.Comment);
            }
        }
    }

    // 起動直後に呼び出すウォームアップ処理
    static void WarmUp()
    {
        try
        {
            // 1. WMI（最軽量）
            var wmi = new ManagementObjectSearcher(
                "SELECT Caption FROM Win32_OperatingSystem");
            foreach (var _ in wmi.Get()) break;

            // 2. レジストリ（軽量キー）
            _ = Registry.LocalMachine.OpenSubKey("SYSTEM");

            // 3. EventLog（最軽量）
            using (EventLog log = new EventLog("System"))
            {
                var _ = log.Entries[0];
            }

            // 4. JIT
            _ = Environment.MachineName;
            _ = DateTime.Now.ToString();
        }
        catch
        {
        }
    }








}
//ドライバーデーター保持
class DriverInfo
{
    public string DeviceName { get; set; }
    public string Version { get; set; }
    public string Provider { get; set; }
    public string InfName { get; set; }
    public DateTime? DriverDate { get; set; }
    public bool IsSigned { get; set; }
    public int ErrorCode { get; set; }

    public bool IsThirdParty =>
        (!string.IsNullOrEmpty(Provider) &&
         Provider.IndexOf("Microsoft", StringComparison.OrdinalIgnoreCase) < 0)
        || (InfName?.StartsWith("oem", StringComparison.OrdinalIgnoreCase) ?? false);

    public bool IsBad =>
        ErrorCode != 0 || !IsSigned;
}

//cfgファイルの保持用
static class CfgTemp
{
    public static string Engine = null;
    public static bool? ExpertMode = null;
    public static byte? MaxData = null;
    public static List<string> MasterLines = new List<string>();
    public static bool? Ran_Sw = null;
}

//クラッシュ・異常終了サマリで使う
class WerGroup
{
    public string P1;
    public string EventName;
    public string P4;
    public string Bucket;
    public DateTime Latest;
    public int Count;
}

class UnifiedEvent
{
    public DateTime Time;
    public string Text;
}

class WerParsed
{
    public string Bucket;
    public string BucketType;
    public string EventName;
    public string P1;
    public string P4;
}




// GUI？「自分、不器用ですから」







